Const限定符

Intro

有时候我们希望定义这样一种变量,它的值不能被改变。为了满足这一要求,可以用关键字const对变量的类型加以限定。

因为const对象一旦创建后其值就不能再改变,所以const对象必须初始化。

默认状态下,const对象仅在文件内有效

某些时候有这样一种const变量,它的初始值不是一个常量表达式,但又确实有必要在文件间共享。解决的办法是,对于const变量不管是声明还是定义都添加extern关键字,这样只需定义一次就可以了:

1
2
extern const int bufSize = fcn();
extern const int bufSize;

如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字。

const的引用

称之为对常量的引用(reference to const)

引用的类型必须与其引用对象的类型一致,但是有两个例外。

第一种例外情况就是在初始化常量引用的时允许用任意表达式作为初始值,只要该表达式的结果能转换成应用的类型即可。尤其,允许为一个常量引用绑定非常量的对象

对const的引用可能引用一个并非const的对象

指针和const

指向常量的指针(pointer to const)不能用于改变其所指对象的值。要想存放常量对象的地址,只能使用指向常量的指针。

指针的类型必须与其所指对象的类型一致,但是有两个例外。第一种例外情况是允许令一个指向常量的指针指向一个非常量对象。

所谓指向常量的指针或引用,不过是指针或引用“自以为是”罢了,它们觉得自己指向了常量,所以自觉地不去改变所指对象的值。

const指针

常量指针(const pointer)

顶层const

指针本身是不是常量以及指针所指的是不是一个常量就是两个相互独立的问题。用名词顶层const(top-level const)表示指针本身是个常量,而用名词底层const(low-level const)表示指针所指的对象是一个常量。

constexpr

常量表达式(const expression)是指值不会改变并且在编译过程就能得到计算结果的表达式。

const method

当你用const关键字修饰一个method,这个指针就会变成一个指向const object,你就不能改变任何成员数据。(除非你使用mutable)。

例子引用自stackoverflow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class CL2
{
public:
void const_method() const;
void method();

private:
int x;
};


const CL2 co;
CL2 o;

co.const_method(); // legal
co.method(); // illegal, can't call regular method on const object
o.const_method(); // legal, can call const method on a regulard object
o.method(); // legal

条款3

尽可能使用const

Use const whenever possible.

1
2
3
4
char *p = greeting;             //non-const pointer, non-const data
const char *p = greeting; //non-const pointer, const data
char* const p = greeting; //const pointer, non-const data
const char* const p = gretting; //const pointer, const data

如果关键字const出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针自身是常量;如果出现在星号两边,表示被指物和指针两者都是常量。

const 成员函数

将const实施于成员函数的目的,是为了确认该成员函数可作用于const对象身上。

1
2
3
4
5
6
7
8
9
10
11
12
13
class TextBlock {
public:
...
const char& operator[](std::size_t position) const{
return text[position];
}
char& operator[](std::size_t position)
{
return text[position];
}
private:
std::string text;
};
1
2
3
4
TextBlock tb("Hello")l
std::cout << tb[0]; //调用non-const TextBlock::operator[]
const TextBlock ctb("World");
std::cout << ctb[0]; //调用const TextBlock::operator[]
1
2
3
4
5
void print(const TextBlock& ctb)
{
std::cout << ctb[0];
...
}
1
2
3
4
std::cout << tb[0];  //没问题 读一个non-const TextBlock
tb[0] = 'x'; //没问题 写一个non-const TextBlock
std::cout << ctb[0]; //没问题 读一个const TextBlock
ctb[0] = 'x'; //错误! 写一个const TextBlock
  • 将某些东西声明为const可帮助编译器侦测出错误方法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数主体。
  • 编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性”(conceptual constness)。
  • 当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。

条款20

宁以pass-by-reference-to-const 替换pass-by-value.

缺省情况下C++以by value 方式传递对象至函数。除非你另外指定,否则函数参数都是以实际实参的复件(副本)为初值,而调用端所获得的亦是函数返回值的一个复件。这些复件系由对象的copy构造函数产出,这可能使得pass-by-value成员昂贵的的操作。

1
2
bool validateStudent(Student s);
bool validateStudent(const Student & s);

后者传递方式效率高:没有任何的构造函数和析构函数被调用,因为没有任何对象被创建。

by reference方式传递参数也可以避免slicing(对象切割)问题。

1
2
3
4
5
void printNameAndDisplay(Window w)   //不正确!参数可能被切割。
{
std::cout << w.name;
w.display();
}

解决切割(slicing)问题的方法,就是以by reference-to-const的方式传递。

1
2
3
4
5
void printNameAndDisplay(const Window * w)   //很好,参数不会被切割
{
std::cout << w.name();
w.display();
}
  • 尽量以pass-by-reference-to-const替换pass-by-value。前者通常比较高效,并可避免切割问题(slicing problem)。
  • 以上规则并不适用于内置类型,以及STL的迭代器和函数对象。对它们而言,pass-by-value往往比较适当。

Java final vs. C++ Const

Java final

  1. 修饰变量:变量不可修改
  2. 修饰方法:方法不可以重载
  3. 修饰类:类不可继承

Clockwise/Spiral Rule

顺时针阅读法,简单的说,就是从变量名开始,顺时针螺旋阅读类型。

1
2
3
4
int * pointer to int
int const * pointer to const int
int * const const pointer to int
int const * const const pointer to const int

const char vs. char const

The difference is that const char is a pointer to a const char, while char const is a constant pointer to a char.

Reference

C++ Primer第五版

https://cdecl.org/

https://stackoverflow.com/questions/751681/meaning-of-const-last-in-a-function-declaration-of-a-class

https://stackoverflow.com/questions/4971286/javas-final-vs-cs-const

More than your eyes can see