顶层const、底层const、函数重载与const形参
2015-05-01 12:01
246 查看
1. 顶层const(top-level const)与底层const(low-level const)
为了更好的研究指针和引用,我们引入了底层const这个名词。注意,底层const是针对C++里面的指针和引用而引入的。我们知道,指针本身是不是常量与该指针所指向的是不是常量,这是两个相互独立的问题。为了区分这两种情况,我们引入两个专有名词:顶层const(top-level const)与底层const(low-level const)。用 顶层const(top-level const)表示指针本身是一个常量,而用 底层const(low-level const)表示指针所指的对象是一个常量。由指针引入,更一般的,顶层const可以表示任意的对象(任何数据类型)是常量。而底层const则与指针和引用等复合类型的基本类型部分有关。
为了检验一下有没有正确理解什么是顶层const(top-level const)与底层const(low-level const),请看几个小例子:
int i = 0; int *const p1 = &i; //不能改变p1的值,因为p1是一个顶层const const int ci = 1; //不能改变ci的值,因为ci是一个顶层const const int *p2 = &ci; <span style="white-space:pre"> </span>//允许改变p2的值,因为p2是一个底层const const int * const p3 = p2; <span style="white-space:pre"> </span>//靠左的是底层const,靠右的const是顶层const const int &r = ci; //用于声明引用的const都是底层const,因为引用本身就是一个顶层const,只能在初始化的时候进行赋值
对于拷贝操作,顶层const没有什么要求,而底层const要求左值必须比右值更严格。
拷贝操作时,对于顶层const,可以把一个顶层const值赋给一个非顶层const变量,也可以把一个非顶层const值初始化给一个顶层const值。如上面例子中:
int *const p1 = &i; //把一个非顶层const值初始化给一个顶层const值 i = ci; //变量ci是顶层const,但变量i不是顶层const p2 = p3; //变量p3是顶层const,但变量p2不是顶层const
拷贝操作时,对于底层const,左值必须和右值相同或更严格。也就是说,对于指针和引用,如果右值是const,则左值必须是底层const。还是上面的例子:
int *p = p3; //error:p3包含底层const,而p没有 p2 = p3; //正确:p2与p3都是底层const p2 = &i; //正确:int*能转换成const int* int &r = ci; //error:r只具有顶层const而不具有底层const,而ci具有底层const const int &r2 = i; //正确:可以把普通变量赋给const int&
需要注意的一点:对于常量对象取地址,得到的也是一种底层const
const int ci = 0; const int* iptr = &ci; //正确:iptr与右值具有同样的底层const int* iptr2 = &ci; //error:对于常量对象取地址,得到的也是一种底层const
2. const形参与函数重载
对于函数调用,和其他初始化过程一样,当用实参初始化形参时,会忽略掉形参的顶层const。这句话的意思也就是说,如果想通过顶层const进行函数重载,是不会通过编译的。函数重载机制会自动假装忽略掉顶层const而保留底层const。也就是说:
void func(const int i); void func(int i);
编译器认为这是同一个函数,而不是函数重载,进而报错|error: redefinition of 'void func(int)'|。
这个例子告诉我们,一个拥有顶层const的形参无法和另一个没有顶层const的形参区分开来。
但是,我们可以通过底层const来进行函数重载。
Record lookup(Account&); //函数作用于Account引用 Record lookup(const Account&); //新函数,函数作用于常量Account引用 Record lookup(Account*); //新函数,函数作用于指向Account的指针 Record lookup(const Account*); //新函数,函数作用于指向Account常量的指针
当然,当我们传递一个非常量对象或者指向非常量对象的指针时,编译器会优先选用非常量版本的函数。
3. 写在最后的题外话(面试常问的问题):
对于函数形参中的引用,尽量使用常量引用。首先,如果使用普通引用,会带给调用者一种误导,即函数可以修改它的实参的值。此外,使用普通引用而非常量引用,也会极大地限制函数所能接受的实参类型。因为我们不能把const对象、字面值或者需要类型转换的对象传递给普通的引用形参。而const引用就可以。相关文章推荐
- C++中形参与const形参的函数重载问题。
- 顶层const与底层const
- C++学习笔记(3):const与&修饰函数形参的作用
- 顶层const与底层const
- C++ 顶层const与底层const的区别
- C/C++的区别(默认值、内联函数、函数重载、const、引用、参数、返回值)
- 顶层const与底层const
- C++的顶层const和底层const的理解
- 顶层const 与底层const
- C++中顶层const和底层const
- 顶层const与底层const
- 函数返回const引用形参的指针问题
- C++之const限定符(顶层const,底层const)
- C++ 学习之函数重载、基于const的重载
- 底层const和顶层const
- iterator的顶层const与底层const
- Const 重载解析(const参数重载 和 const成员函数重载)
- 顶层const和底层const
- CString 强制转化为const char*,作为形参带入函数内出错。用CW2A解决
- CppPrimer--顶层const与底层const