引言
在C和C++中,限定符const的应用可谓十分常见。我在重现教材书籍code的时候,老是在const的问题上犯迷糊。今天刚忙完导师布置的任务,闲着就像把这个问题说清楚,以后自己再有问题也好自我反省下。
首先在C语言中,const是一个限定符,它用来限定一个变量不被改变。使用const在一定程度上可以提高程序的健壮性,另外,在观看别人代码的时候,清晰理解const所起的作用,对理解对方的程序也有一些帮助。那么具体来看,const被用在什么地方呢。
我将从以下4点来讲解const限定符的用法:
1、对象与const
2、引用与const
3、指针与const
4、函数与const
1、对象与const
这是最直观的用法。比如我们有以下代码:
const int bufSize = 512;
定义bufSize为常量且初始化为512。此后,任何尝试修改bufSize的尝试都会导致编译错误,例如 bufSize = 100;
此外,还要注意的是:
const std::string hi = "hello!"; //正确 const int i, j = 0; //错误,i 没有初始化
因为作为const常量在定义后就不能修改,所以定义时必须初始化。
2、引用与const
先复习一下引用。引用就是它绑定的对象的另一个名字,例如:张三有个小名“阿三”,叫“阿三”就是叫张三这个人,那么“阿三”就是张三的别名,即引用。
引入const后,引用有了一些有趣的规则,这里讲一下。
const引用:const引用是指向const对象的引用,例如:
const int r = 100; double r1 = 100.0; const int &p1 = r; //正确,const引用可以绑定到const对象上 const int &p2 = r1; //正确,const引用可以绑定到数据类型相关的const对象上 const int &p3 = 100; //正确,const引用可以绑定到右值
与之对应的,非const的引用只能绑定到与该引用相同类型的对象上。
3、指针与const
同样简单先复习一下指针的知识。指针也是一种变量,它保存的是一个对象的地址(注意:指针也有自身的地址,该地址中的数据就是该指针指向的地址)。
指针与const关系比较复杂,我这里借鉴《C++ Primer》上的顺序,非3小点来解释这个问题。
1)指向const对象的指针
我们知道,指针可以用来修改它所指向的对象的数据。但是如果它指向一个const对象,那么自然不允许被修改。C++语言中强制要求:指向const对象的指针必须具有const特性。
for example:
const double *pt; //a point to a double that is const
这里的pt是指向一个const对象的指针。值得注意的是,这里仅仅说明了这个指针指向的数据类型,而没有说这个指针本身就是const,因此定义的时候也不一定要初始化,同理如果有必要,还能在接下来的代码中将pt指向其他的const对象。指针本身是const会在接下来的几点中讲到。
这个规则对void指针也成立,即:必须使用const void*类型的指针保存const对象的地址
但是,C++允许把非const对象的地址赋给指向const对象的指针,例如:
double dval = 3.14; const double *pt = &dval;
这样一来,不能通过指针pt来修改dval,但是仍然可以直接修改dval。
《C++ Primer》上有句话也许可以用来帮助记忆这个“指向const对象的指针”:这种指针可以理解为“自以为指向const的指针”。
2)const指针
上一点中,指向const对象的指针不能说明指针本身就是const,那么const指针就是说:指针本身就是const的,不能用它来指向初始化对象以外的对象
const指针语法格式:
int errNumb = 0;
int *const curErr = &errNumb;
可以看到,这种指针必须初始化。但是同样的,const指针并没有说明:能否使用该指针来修改它指向的对象的值。能否修改取决于该对象的类型。
3)指向const对象的const指针
有了前两点的铺垫,这种指针就很好理解了。
const double pi = 3.14159;
const double *const pt = π
这里,pt既不能修改pi的值,也不能更改pi的指向。我们可以从左到右阅读上述的声明语句:
pt首先是一个const指针,指向double类型的const对象
P.S 在《C++ Primer》上有这么一个例子值得讨论,这里拿来讲下。假设有下面的句子:
typedef string *pstr;
const pstr cstr;
问这里的csr变量是什么类型的。很多人看到这里,都会认为第二句是这么展开的 const string *cstr; 因此它就是一个指向string类型的const对象的指针。这其实是错误的认识,这种错误源自于认为typedef单纯的是做文本扩展了(文本扩展那是宏定义的事)。这里应该这么理解:
const修饰的是pstr类型,而pstr是什么类型呢?typedef告诉我们它是一种string*,就是指向string类型的指针,那么这里的cstr就是一种指向string类型的对象的const指针。即第二句应该这么还原: string *const cstr;
这里同时也是告诉我们,typedef做的事情是对类型的提升。
4、函数与const
有了前面3点的铺垫,函数的const就很好理解了,直接举个例子吧
const int func(const int &a) const{ ... }
函数返回一个const对象,他的参数是一个const的引用,它是一个const的常量函数,也就是说,该函数不会修改调用函数的实参。
5、后记
今天刚好遇到一个const常量成员函数的问题,不妨贴上来和大家分享下。
错误代码是这样的,有兴趣的可以拿回去编译下。
1 #include<iostream> 2 using namespace std; 3 4 class Point3d{ 5 public: 6 Point3d(float x=0.0, float y=0.0,float z=0.0) 7 :_x(x), _y(y), _z(z){} 8 9 float x() {return _x;} 10 float y() {return _y;} 11 float z() {return _z;} 12 private: 13 float _x,_y,_z; 14 }; 15 16 inline ostream& 17 operator<<(ostream &os, const Point3d &pt){ 18 return os<<"("<<pt.x() << "," <<pt.y() <<"," <<pt.z() << ")"; 19 20 } 21 22 int main(){ 23 Point3d pd(1,2,3); 24 cout<<pd; 25 }
我使用DEV给我报的错的这样的:
也就是说:
不能将“this”指针从“const Point3d”转换为“Point3d &”----转换丢失限定符
简而言之,第17行重载的<<操作符使用了const Point3d对象,那么const对象必须调用const成员函数,于是我的x(),y(),z()都没有声明成const函数,于是报错。
在这里我们把每个函数填上const限定符就能解决了。