const 允许你指定一个语义约束,而编译器会强制执行这项约束,确保某个对象不会被改变。如果某个对象在执行中不应该(或者不会)被改变,就应该使用 const。
一. const 修饰变量
看下面一段代码:
1  | 
  | 
const 修饰指针的时候,const 在 * 号右边表示指针是 const 的,在 * 左边,表示 *p(即指针指向的对象),是 const 的。在 STL 迭代器的应用中,map<int, int>::const_iterator itr 相当于 const T* itr,即迭代器(指针)itr 指向的对象 *itr 是 const 的,而 const map<int, int>::iterator itr 相当于 T* const itr,即迭代器 itr 是 const 的。
在上边代码中,若 a 的值被改变了,那么除 b 外,*c, *d, *e, *f, *g 都会改变,因为 b 的赋值是 copy 赋值立即数,之后的 b 就跟 a 没有关系了,但对 c-g 而言,修饰它们的 const 仅仅表示,不能修改指针的值或者通过 *p 修改指针指向的变量,但 a 并不是 const data,所以可以通过修改 a 的值来改变指针 cdefg 指向的值。
使用 const 需要注意,const 对象必须初始化(因为它不能改变不能接受再次的赋值)(但并不一定在声明的同时给予初值)。在函数中声明  const 需要立即给初值,在类 class 中声明的时候,不能立即赋值,这是因为 const 数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的(这点与 static 相反),类可以创建多个对象,相应的 const 成员的初始化只能在类的构造函数的初始化表中进行:
1  | class Stuff {  | 
要想建立在整个类中都恒定的常量,应该用类中的枚举常量或者 static 来实现。
二. const 修饰成员函数
const 成员函数的声明:const 关键字只能放在函数声明的尾部!
1  | int func() const {  | 
 const 修饰的成员函数不能修改这个类对象的任何的数据成员(准确地说是非静态数据成员)。
 const 成员函数不能调用非 const 成员函数,因为非 const 成员函数可以会修改成员变量。
  在设计类的时候,一个原则就是对于不改变数据成员的成员函数都要声明为const 成员函数。有 const 修饰的成员函数(const 放在函数参数表的后面,而不是在函数前面或者参数表内),只能读取数据成员,不能改变数据成员;没有 const 修饰的成员函数,对数据成员则是可读可写的。
这样做好处在于,首先,类的接口更容易被理解,一看便知函数是读操作还是有写操作;其次,将成员函数声明为 const 使得该函数操作 const 对象成为可能,因为常量(即 const)对象可以调用 const 成员函数,而不能调用非 const 修饰的函数。
ps:之所以我们希望函数可以操作 const 对象是因为处于程序的效率考虑,传引用或者指针往往比传值要好,传引用的时候为了防止对象被修改,往往要声明为 const 引用或指针,const 对象不能调用非 const 函数,所以,尽可能的将只有读操作的函数声明为 const 函数。
注意:两个成员函数如果只是常量性不同,是可以被重载的,原因在于函数的形参列表里隐藏有 this 指针,const 函数里 this 指针是指向 const 对象的指针,而非 const 函数里的 this 指针是正常版本的指针。
1  | 
  | 
const 对象默认调用 const 成员函数,非 const 默认调用 const 成员函数,类中只有一个函数存在的情况下(我们删掉 non_const 版本的 getNation()),non_const 的对象也可以调用非 const 成员函数。
三. const 修饰函数返回值
如果给以“指针传递”方式的函数返回值加 const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加 const 修饰的同类型指针。例如函数:
1  | const char * GetString(void);  | 
如下语句将出现编译错误:
1  | char *str = GetString();  | 
正确的用法是:
1  | const char *str = GetString();  | 
如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加 const 修饰没有任何意义。
例如不要把函数:
1  | int GetCnt();  | 
写成:
1  | const int GetCnt();  | 
同理不要把函数(T为自定义数据类型):
1  | T getT(void);  | 
写成
1  | const T getT(void);  | 
如果返回值不是内部数据类型,将函数 T getT(void) 改写为 const T & getT(void) 的确能提高效率。但此时千万千万要小心,一定要搞清楚函数究竟是想返回一个对象的“拷贝”还是仅返回“别名”就可以了,否则程序会出错。
函数返回值采用“引用传递”的场合并不多,这种方式一般只出现在类的赋值函数中,目的是为了实现链式表达。(除了重载操作符外一般不要将返回值类型定为对某个对象的 const 引用!)
例如:
1  | class A {  | 
如果将赋值函数的返回值加 const 修饰,那么该返回值的内容不允许被改动。上例中,语句 a = b = c 仍然正确,但是语句 (a = b) = c 则是非法的。