const可以用来定义常量。
const写在数据类型前或后是等价的,如下两种写法是等价的。
const int a=10;
int const b=20;
常量将不能被修改,因此在定义时必须初始化。
以下写法将不能通过编译。
const int a;
const更多复杂的用法体现在它和引用或指针的组合中。
int a=10;
int &b=a;//普通引用
const int &c=a;//常量引用,将不能通过引用修改变量
以下代码第四行将报错。
int a=10;
int &b=a;//普通引用
const int &c=a;//常量引用,将不能通过引用修改变量
c=5;
如果const修饰指针,则该指针将不能再指向其它对象,即常量指针。
以下是常量指针,它可以修改对象的值但是不能更换指向的对象。去掉注释部分将报错。
int a=10,b=20;
int *const xp=&a;
*xp=5;
//xp=&b;
如果const修饰的是指向的对象,则不能通过该指针修改它指向的对象,即指向常量对象的指针。
以下是指向常量对象的指针,它可以更换指向的对象但不能修改该对象的值。去掉注释部分将报错。
int a=10,b=20;
const int *xp=&a;
//*xp=5;
xp=&b;
以上两种的定义的区别要看const在*前还是后。在*前就是指向常量对象的指针,在*后就是常量指针。
如果const既修饰指针也修饰指向的对象,它将是指向常量对象的常量指针。既不能更换指向的对象,也不能修改指向对象的值。
int a=10,b=20;
const int *const xp=&a;
//*xp=5;
//xp=&b;
const可以用来定义常量,保证对象不被修改。于是在对象赋值时就出现问题了。
对于普通变量,这里没有问题,因为赋值操作是完成一次拷贝,拷贝完成后两个对象就没有任何关系了。
所以,以下两种代码都可以通过。
int a=10;
const int b=a;
const int a=10;
int b=a;
在以上代码中a和b的修改都不会影响到对方。
所以得出结论如果赋值运算符左侧对象是普通对象(非引用和非指针),我们就不用管右侧对象是否有const了,因为拷贝完成以后两个对象已经完全独立了。
但是如果左侧是引用或指针就不一样了,因为对他们完成赋值以后,他们还是跟原对象有关联的。试想,如果赋值右侧是一个const int,而左侧是int &,如果我们可以通过该引用来修改原常量对象,这将违背const的规则。事实上,c++将拒绝通过这种行为。
即如下代码是无法通过编译的。
const int a=10;
int &b=a;
把引用也改成const就可以了。
const int a=10;
const int &b=a;
int a=10;
const int &b=a;
对于普通引用,它绑定的对象不能是常量,因此以下代码是错误。
int &a=10;
而对于常量引用,它绑定的对象可以是变量也可以是常量,以下代码是对的。
const int &a=10;
以上规则可以扩展至指针。
普通指针将不能指向常量对象,以下代码是错的。
const int a=10;
int *p=&a;
正确写法:
const int a=10;
const int *p=&a;
注意上面这是指向常量对象的指针,而非常量指针。常量指针不强调指向对象是否常量,因此下面的代码不能通过编译。
const int a=10;
int *const p=&a;
通过上面的一些例子,我们隐约可以感受到一丝的规律。如果const的修饰作用将涉及另一个对象(如果const修饰引用,修饰指针指向的对象)它就会有问题,否则如果只关系自己(如const修饰普通变量,修饰指针)那就没问题。在《c++ primer》一书中,作者将前者称为底层const,后者称为顶层const。
顶层const:
const int a=10;
int b=1;
int *const xp=&b;
int *yp=xp;
显然,普通的常量对象、常量指针,这里的const都是顶层const。仅被顶层const修饰的对象在赋值时会去掉const属性,因此它可被赋值给常量或非常量对象。如上第四行。
底层const:
int a=10;
const int *xp=&a;
const int *yp=xp;
const int b=1;
const int &q=b;
显然,常量引用、指向常量对象的指针,这里的const都是底层const。仅被底层const修饰的对象在赋值的时候const属性将被保留,因此它仅可被赋值给具有底层const的对象。如上第三和第六行。
如果理解了底层const和顶层const,大部分和const有关的问题就可以解决了。
下面我们用const的规则来解释一下c++中class中的常量函数。
众所周知,类的常量对象只能调用常量成员函数,不能调用非常量成员函数。
为什么呢?
实际上对象调用成员函数是通过this指针实现的。
this默认是一个常量指针,即永远指向该对象,这不会有问题,我们不能更换this指向的对象。调用该函数的this指针是一个常量指针,它有顶层const但没有底层const,因此对于常量对象是不能绑定到该this指针上的。所以常量对象不能调用非常量成员函数。
如果你在成员函数后面加一个const,即将其定义为常量成员函数,将意味着调用该函数的this是一个指向常量对象的常量指针。调用的时候自然要传参数啦,无论该对象是常量还是非常量都可以传给该this指针,因此常量对于常量成员函数,常量或非常量对象都可以调用。