C++ const 允许指定一个语义约束,编译器会强制实施这个约束,允许程序员告诉编译器某值是保持不变的。如果在编程中确实有某个值保持不变,就应该明确使用const,这样可以获得编译器的帮助。
-
一、const常量的初始化
int main(){ int a1 = 1; // 非常量,可以改变其值 a1 = 2; // 此时a1的值为2 const int a2; // 错误,const常量必须初始化 const int a2 = a1; // 正确 a2 = 0; // 错误,const常量不可改变 }
-
二、const引用
1、对常量的const引用
对const常量的引用,只能是const引用const int ci = 1024; // 初始化常量ci const int &r1 = ci; // 正确,用常量引用绑定常量ci int &r2 = ci; // 错误,试图用一个非常量引用绑定一个常量对象
2、对非常量对象的const引用
对非常量对象可以用一个常量引用绑定它,并且不可通过常量引用去改变原变量的值。但是被绑定的非常量对象是可变的,并且改变后其const引用也会改变。int i = 42; // 定义变量i=42 const int &r1 = i; // 正确,定义常量引用r1,并绑定到变量i上 const int &r2 = 42; // 正确,定义常量引用r2 const int &r3 = r1 * 2; // 正确,定义常量引用r3 int &r4 = r1 * 2; // 错误,视图用非常量引用绑定到常量上 int &r5 = i; r5 = 1; // 正确,用非常量的引用绑定变量i并改变i的值为1 r1 = 42; // 错误,不能通过常量引用改变原变量,此时r1=1
-
三、const和指针
(1)只有一个const,如果const位于*左侧,表示指针所指数据是常量,不能通过解引用修改该数据;指针本身是变量,可以指向其他的内存单元。
(2)只有一个const,如果const位于*右侧,表示指针本身是常量,不能指向其他内存地址;指针所指的数据可以通过解引用修改。
(3)两个const,*左右各一个,表示指针和指针所指数据都不能修改。
int a1 = 1; const int *r1 = &a1; // (1)const位于*左侧 int const *r2 = &a1; // (1)同上 int *const r3 = &a1; // (2)const位于*右侧 const int *const r4 = &a1; // (3)*左右各一个const int const *const r5 = &a1; // (3)同上
值得一提的是,在(1)中所说,const位于*左侧时表示指针所指数据为常量,而事实上r1指向的a1是变量,const只是限定了r1对a1的权限,a1本身是否可变与r1无关。
顶层const和底层const:
顶层const表示指针本身是个常量(即上述2),而底层const指的是指针所指的是一个常量(上述1)。 -
四、const引用和const指针
1、const的修饰作用实际上是给引用和指针设置的权限,也就是说const引用和const指针(底层)不能用于修改其绑定的变量。而变量本身是否能修改要看绑定的是否是const修饰的常量。
2、对于const修饰的常量进行引用和指针的绑定时,必须是const引用和const指针(底层)。
3、而对非常量进行引用和指针的绑定时,可以是const的也可以是非const(也就成了普通的引用和指针)的。 -
五、const成员函数
首先const成员函数的定义方法是在成员函数的参数列表后紧跟const关键字。C++primer上对此处const的作用定义为:修改隐式指针this指针的类型。
默认情况下,this指针的类型是:指向类类型非常量版本的常量指针。以书中的Sales_data类举例,this的类型为Sales_data *const。根据(三)中我们的总结,对于指向非常量类型的指针const,我们不能把它绑定到一个常量对象上,也就是说——不能在一个常量对象上调用普通的成员函数。
此时,常成员函数的作用就体现出来了,const关键字隐式的把非常量版本的指针,修改成了常量版本——const Sales_data *const。由此,得出如下几个结论:
① 常量对象,以及常量对象的引用或指针都只能调用常量成员函数
② 非常量对象可以调用常量成员函数,但常量成员函数不改变对象中数据成员的值
③ 一个例外,用mutable(意为易变的,其承诺被修饰的变量永远可变)修饰的成员变量,可以被常量成员函数修改