C++中const顶层指针与底层指针
1.为什么要设置const?
- C++中我们希望某个对象的值不能被更新,在前面加上关键词const,若是有改变这个对象值的意向,编译器会报错。事实上程序员如果认为某个对象是只读的,不会在写程序过程中刻意去改变它,但是难免有操作导致对象的值改变,加上关键词const后,把程序可能出现的某些错误集中在编译过程解决。
- 编译器对const常量的处理是在预编译的时候把折叠的const常量释放。
2.const指针
对于一个指针来说,加上const的意义是什么?
- 不希望改变这个指针的指向,也就是说不希望这个指针的值改变。
- 不希望改变这个指针所指向的值。
例如:
int a=4;int b=5;
第一种指针
int *const ptr=&a;
当有改变ptr这个指针指向的意图时,编译器会报错。
ptr=&b;//错误
这种指针我们叫他顶层指针。
第二种指针
const int *ptr=&a;
当有意向通过*ptr改变a的值时,编译器会报错。
(*ptr)=6;//错误
这种指针我们叫他底层指针。
对于底层指针来说,不希望改变它指向的值的意思是不能通过这个底层指针改变它指向的值。
例如:
#include<iostream>
using namespace std;
int main(){
int a=4;
const int *ptr=&a;
//*ptr=5; //去掉注释会报错,原因:不能通过底层指针改变其指向值
a=5;//改变a的值
cout<<a<<endl;
return 0;
}
实际上a的值还是改变了,只不过绕过了底层指针的影响。
底层指针自以为不改变*ptr,如果ptr指向一个变量的话还可以有其他途径改变a的值。
3.const指针间的赋值
对于一个顶层const指针,它可以被当成右值赋给一个同类型的指针。
例如:
int *const ptr=&a;//ptr具有顶层属性
int *temp1=ptr;//正确
int *const temp2=ptr;//正确
但是对于一个底层const指针,如果等号左右不同类型则编译器会报错。
例如:
const int *ptr=&a;//ptr具有底层属性
int *temp=ptr;//错误
上文说过,底层指针自以为不改变*ptr,编译器不会允许它赋值给一个有可能改变 *ptr的指针。
如果把ptr赋给temp,
- 把temp申请成为和ptr同类型的底层指针。
去掉ptr的底层属性。
int * temp=const_cast< int *>(ptr);//解除底层const
注意:如果ptr指向一个常量,const_cast去除ptr的底层属性时,也会不小心把常量的底层属性也一起去除了。
例如:
//Dev-C++ 5.4.0
#include<iostream>
using namespace std;
int main(){
const int a=4;
const int *ptr=&a;
int *temp=const_cast< int * >(ptr);
//a=5;//去掉注释会报错
*temp=5;
cout<<"temp的地址:"<<temp<<" temp指向的值:"<<*temp<<endl;
cout<<"a的地址:"<<&a<<" a的值:" <<a<<endl;
return 0;
}
运行结果
为什么temp指向的值为4?
因为对于const int a=4,编译器在编译阶段把代码带有a值的替换成了4,但是当运行的时候*temp的值的确是被赋值成了5。
运行时0x28ff04地址的值实际是5。
编译器只是在编译阶段将const常量替换成了他们所对应的值,
运行时const常量空间的访问是不设限的
然而:
const int a=5;
int *ptr=&a;
cout<<(*ptr+1)<<endl;
//这样连编译都通不过
//[Error] invalid conversion from 'const int*' to 'int*' [-fpermissive]
改成这样:
#include<iostream>
using namespace std;
int main(){
const int a=5;
int *ptr=(int *)&a;
*ptr=6;
cout<<"a的值:"<<a<<endl;
cout<<"ptr指向的值:"<<*ptr<<endl;
return 0;
}
运行结果
4.const引用
const引用的使用实际上与使用const指针一样的。
因为const引用的底层部分被隐藏了,地址更改不了,可以看作是顶层的。
底层const引用一般为
int a;
const int &b=a;
实际上对于指针ptr,*ptr也可以看作是ptr指向的值的引用,
在C++11中
decltype( *ptr )b=a;
可以看到b实际是个引用类型,绑定了a。