参考自:https://www.cnblogs.com/lustar/p/7429757.html (有几处不正确,经实验我已经改过来了,下面红色标出)
引入两个概念:顶层const和底层const。可以简单的认为,声明或者定义的对象(指针也是对象),如果对象本身是const的,那么就是顶层const,否则是底层const。从概念上并不容易搞清除,看几个例子:
(1)const int i=1;//i本身是const,顶层
(2)const int* p=&i;//p本身不是常量,底层 (判断依据 :const修饰的是*p0, 所以*p0是常量,不能修改。但p0不是常量,所以能修改。)
(3)int* const p=&i;//p本身是常量,顶层
(4)const int* const p=&i;//左边是底层,(判断依据 :const修饰的是*p, 所以*p是常量,不能修改。但p又被const修饰,所以不能修改。) ,右边是顶层
对于常量引用,同样有两种:
const int i = 1; const int& a = i; int& const b = i;//error const int c = i;
对于函数的参数类型,例如:
int foo(const int& t);
int foo(int& t);
按照函数重载的规则,以上两者只能存在一个,建议采用const版本。一部分原因,在于变量在传输的时候,会忽略顶层const的属性,而非常量可以转换为常量。另外,这样定义的适用性更强。
还有一种情况,就是类成员函数的const,例如:
struct base
{
int num;
int getNum() const{return num;}//若没有const,那么对于const base将无法使用该函数
};
这样的const,作用是把类对象的this指针转换为常量。在不需要修改对象成员的时候,可以保证成员不被修改,另外,与上一种相同,当对象为常量对象时,只有这样才能正确返回。