C++ 中关于 const 的知识点不少,在这里作个总结。express
const 关键字修饰的变量在建立后值就不能改变了,所以必须在建立时进行初始化。函数
对象的类型决定了可以在对象上进行哪些操做。对 const 对象来讲,只能使用那些不能改变对象状态的操做。this
若是 const 对象是由一个编译时常量(compile-time constant)进行初始化的:spa
const int bufSize = 512; // input buffer size
那么编译器通常会在编译时用这个常量替换该 const 变量。即编译器把全部用到 bufSize 的地方替换为512。为了进行这种替换,显然编译器须要知道 const 变量的初值是多少,这就须要检查 const 变量是如何被定义并初始化的。指针
若是程序分布在多个文件中,为了对变量的值进行替换,编译器在编译每一个用到该 const 变量的文件时,都须要知道该 const 变量是如何被定义的,这就要求在每一个用到该 const 变量的文件中都对该变量进行定义(define 而不是 declare),然而一个变量是不能被屡次定义的。为解决这个矛盾,规定 const 变量的做用域是文件自己(local to file)。当咱们在多个文件中定义名字相同的多个 const 变量时,就像在每一个文件中定义了一个不一样的变量同样。code
(对于通常变量来讲只能在一处进行定义,在其余文件中使用时须要使用 extern 关键字进行声明。)对象
有时咱们想在多个文件中共享一个 const 变量,可是该 const 变量的初始化器(initializer)并非一个常量表达式(constant expression,常量表达式的值在编译时就能够被肯定,如一个数字或算术表达式,一个经过 constant expression 进行初始化的 const 变量也是一个 constant expression)。在这种状况下,咱们不但愿编译器在每一个用到该 const 变量的文件中都单独生成一个新的变量。咱们但愿在一个文件中进行定义,在其余文件中进行声明。要达到这一目标,须要在定义和声明时同时使用 extern 关键字(不然就是 local to file)。ip
// file_1.cc
extern const int bufSize = fcn();
// file_1.h
extern const int bufSize;
下面用代码具体演示一下。若是是 nonconst 的变量,则只能在一处进行定义。ci
// file_1.cc
#include <cstdio>
int bufSize = 512;
void print();
int main() { print(); return 0; }
// file_2.cc
#include <cstdio>
int bufSize = 100;
int print() { printf("\n%d\n", bufSize); return 0; }
编译时报错:multiple definition of bufSize'
。若是将两个 bufSize 中的一个改成 const,或两个都改成 const,则编译经过,输出 100。作用域
// file_1.cc
#include <cstdio>
const int bufSize = 512;
void print();
int main() { print(); return 0; }
// file_2.cc
#include <cstdio>
extern const int bufSize;
int print() { printf("\n%d\n", bufSize); return 0; }
若是是这样,file_1 中进行定义时没有加 extern 关键字,则编译时报错:undefined reference to bufSize'
。
若是将两个文件中的 const 关键字都去掉,或者给 file_1 中的 const 变量也加上 extern 关键字,编译成功,输出512。
对 const 变量的引用
像普通变量同样,也能够将一个引用绑定到一个 const 变量。对 const 变量的引用一样不能改变该 const 变量的值。
const int ci = 1024;
const int &r1 = ci; r1 = 42; // 错误,不能经过对 const 的引用改变 const 变量的值
int &r2 = ci; // 错误,不能用 nonconst reference 引用 const 变量
指针,const 和类型别名(type aliases)
考虑下面的代码:
typedef char *pstring; const pstring cstr = 0; const pstring *ps;
const 修饰的是 pstring,而 pstring 的类型是指向 char 的指针,因此 const pstring 是一个 constant pointer to char,而非 pointer to const char。
不能经过直接替换来理解类型别名:
const char *cstr = 0; // 这是对 const pstring cstr 的错误理解
const 参数
进行赋值时,top-level const 会被忽略,因此如下两个函数定义不能同时存在:
void fcn(const int i) {}
void fcn(int i) {}
这两个函数可以接收一样的参数,因此不算重载。
能够用 nonconst 对象对 low-level const 对象进行初始化,但不能用 low-level const 对象初始化 nonconst 对象。(low-level const 指针认为其指向的对象是 const 对象)。
int i = 42;
const int *cp = &i; // ok: cp 是 low-level const,能够用 nonconst 进行初始化,但不能经过 cp 改变 i 的值
const int &r = i; // ok: reference to const,不能经过 r 改变 i 的值
const int &r2 = 42; // ok
int *p = cp; // error: 不能用 low-level const 初始化 nonconst,但能够用 const_cast 强制转换
int &r3 = r; // error
int &r4 = 42; // error: 不能经过 literal 初始化 plain 的 reference
若是可能就应该使用 reference to const
不用 reference to const,会让调用者错误地认为函数会改变参数的值。并且不用 reference to const,会限制可以传递的参数类型。不能向普通的 reference 传递 const 对象,不能传递 literal,不能传递须要转换的类型。
const 成员函数
成员函数的参数列表后面能够加关键字 const,做用是修饰 this 指针。
this 指针默认是 const pointer to nonconst object,故不能指向 const object,也就是说 const 对象的方法没法被调用。将成员函数变为 const 成员函数,其 this 指针隐含参数变为 const pointer to const object。const 成员函数不能改变对象状态。const 对象只能调用 const 成员函数。
注意 const 成员函数若是返回 *this,那么返回的是一个 const 对象,不能在返回对象基础上继续调用 nonconst 的成员函数。为解决这一点能够基于成员函数的 constness 对成员函数进行重载。