在c++ Primer中:
顶层const(top-level const)指对象本身是一个常量
更一般的是,顶层const可以表示任意类型是常量,这一点对绝大多数类型都适用,如类、数据类型、指针
底层对象(low-level const)指指针指向的对象是一个常量
当执行对象的拷贝操作的时候,常量是顶层const还是底层const区别明显,其中顶层const不受什么影响。
另一方面,底层const的限制不能忽视。当拷贝对象时,拷入和拷出的对象必须具有相同的底层const资格,或者两个对象的类型可以互相转换,通常非常量可以转换成常量,反之不行。
//p3是一个底层const也是一个顶层const
int *p = p3; //错误;p3包含底层const含义,p没有
p2 = p3; //正确,p2和p3都是底层const
p2 = &i; //正确,int *能转换成const int *
int &r = ci;//错误普通的&r不能绑定到常量上
const int &r2 = i;//正确const int &可以绑定到i上
我们可以使用非常量初始化一个底层的对象,但是反过来不行;
同时一个普通的引用必须用同类型的对象进行初始化
int i = 43;
/*cp是一个底层const,i是一个非常量(变量)
这样的用法是正确的,但是不可以通过cp改变i的值,因为cp是底层const
*/
const int *cp = &i;
/*
/*r一个底层const,i是一个非常量(变量)
这样的用法是正确的,但是不可以通过r改变i的值,因为r是底层const
*/
const int &r = i;
/*r2一个底层const,i是一个非常量(变量)
这样的用法是正确的,但是不可以通过r2改变i的值,因为r2是底层const
*/
const int &r2 = 42;
将同样的初始化规则应用到参数传递上可得如下形式:
int i = 0;
const int ci = i;
string::size_type ctr = 0;
reset(&i); //调用形参类型是int*的reset函数
reset(&ci) //错误,不能用const int*类型的对象初始化int*
reset(i) //调用形参类型是int的reset函数
reset(ci) //错误,不能用const int类型初始化int
reset(42) //错误,不能把普通引用绑定到字面值上
reset(ctr) //错误,类型不匹配,string::size_type是无符号类型
尽量使用常量引用 把函数不会改变的形参定义成普通的引用是一种比较常见的错误,这会导致:
- 给函数的调用者一种误导,即函数可以修改它的实参的值
- 使用引用而非常量引用也会极大地限制函数能够接受的实参类型。就像上文所述,我们不能把const对象、字面值或者需要类型转换的对象传递给普通引用的实参,因为底层const对象不能初始化变量。
函数传递中字面值可以初始化底层const对象但是不能初始化变量。如:
"hello world"可以初始化const string &s形参,但是不能初始化const string s形参