C++中const限定符
const基础
C++中的const,用于定义一个常量,这个常量的值不能被修改。因为const对象一旦创建就不能修改,所以const对象必须初始化。const常量特征仅仅在执行改变其本身的操作时才会发生作用,所以在进行其他操作时,const常量和普通变量没有区别。
它的声明方式为
void main() { int a; const int b; //int型常量 int const c; //同上 const int* d; // 指针常量,d为const,指向常整型数的普通指针(所指向的内存数据不能修改,但本身可以修改) int* const e; // 指向普通int型量的常量指针。int*为const(指针变量不能被修改,但是它所指向内存空间可以被修改 const int* const f;//指向int常量的常量指针(指针和它所指向的内存空间,都不能被修改) }
const与指针
因为指针本身也是一个对象,所以和指针相关的const可以分为两种:
1.指针常量(pointer to const )
指针常量也就是指向常量的普通指针。也就是不能通过指针修改所指向的对象,但可以修改指针本身的指向。这里要注意的是指针常量不要求指针一定要指向一个const常量,只是指不能通过指针修改所指向的对象。它的声明形式为:
const int* d;
2.常量指针(const pointer)
常量指针是指向一个对象的常量指针。常量指针必须初始化,因为常量指针不能更改其指向。这里要注意的是普通指针(包括非常量指针和常量指针)无法绑定一个常量对象。要想指向一个常量对象,必须使用指针常量,但是指针常量所指向的对象却不一定是常量对象,这里要注意区分。为了使常量指针能够指向常量对象,于是有了指向常量的常量指针。声明如下:
int* const e; // 指向普通int型量的常量指针。int*为const(指针变量不能被修改,但是它所指向内存空间可以被修改 const int* const f;//指向int常量的常量指针(指针和它所指向的内存空间,都不能被修改)
指针常量和常量指针的区分
指针常量(pointer to const )和常量指针(const pointer)会比较容易混淆。我在网上查看时,找了一个我觉得不错的区分方法,即 “以*为中间划一条线,看const修饰谁就谁就是常量”。如,const int * d;以*划线后结果为”const int“,const修饰的是int,也就是指针所指向的对象,所以d是一个指针常量。同理对于int * const e; 划分后为const e,const修饰指针e,所以e是一个常量指针。
const的引用
不同于指针本身是一个对象,引用并不是一个对象,所以不存在常量引用,只有对常量的引用(reference to const),但是在c++中,引用一旦绑定到其他对象上,就不允许改变绑定的对象,所以引用本身具有const的特性。有的地方可能会将对常量的引用简称为常量引用,这里要知道,常量引用指的是什么。
一般要求,引用的类型必须与被引用的对象类型相同,但在有关const的引用中有例外的情况:即初始化常量引用时允许使用任意可以转换为引用类型的表达式作为初始值。例如
int i = 10; const int &r1 = i; //正确,可以将const int& 绑定到一个普通int对象 const int &r2 = 10; //正确,r2是一个常量引用 const int &r3 = r1 * 2; //正确,r3是一个常量引用 int &r4 = r1 * 2; //错误,r4是一个普通非常量引用
能够这样初始化的原因是:对于const int &r1 = i; 这样的代码,编译器会拆解为:
const int temp = i; const int &r1 = temp;
这里系统生成了一个临时量对象temp,然后将r1绑定在了temp上。这里很多人可能会有疑问,为什么可以将引用绑定到一个临时对象上?C++的标准规定了:当一个临时对象被绑定到一个引用时,临时对象的生命周期 >= 引用的生命周期("如果一个临时性对象被绑定于一个引用,对象将残留,直到被初始化之引用的生命期结束"(见《Inside The C++ Object Model》page275))。但必须要注意的是只有常量引用才能绑定到临时量对象。所以 int &r4 = r1 *2 ;是不能通过编译的。同时也要认识到,常量引用可以引用一个非const的对象。
类中的const
在c++类中,可能出现const的地方有:
- 常量对象
- 常量成员数据
- 常量成员函数
1.常量对象
常量对象,就是声明类的实例时加上const,如下:
const MyClass myclass;
2.常量成员数据
就是声明在类中的const数据,和在类外一样。
3.常量成员函数
常量成员函数的主要作用是可以被常量对象调用。常量成员函数的声明有两种,如下:
void func(int a) const{};
void const func(int a){};
这两种方式没有区别,它们的本质都是void func (const ClassName *this, int a); const 修饰的不是形参a,而是this(this是一个额外的隐式参数,用来访问当前对象,它的类型默认是指向类类型非常量版本的常量指针),与const所写的位置无关。因为c++对类的this指针做了隐藏,本质上,const指针修饰的是被隐藏的this指针所指向的内存空间。这里的func()函数,如果不添加const关键字,就是一个普通的成员函数,此时this的类型是ClassName* const,即this是一个常量指针,但是一般的常量指针是不能指向常量的,所以如果要使this指向一个常量对象,必须要把this定义成const ClassName* const,即指向常量的常量指针。在类成员函数的参数列表之后添加const关键字,就相当于把this定义成const ClassName* const,只有这样常量对象才能调用这个函数。
一些总结:
1)const成员函数可以访问非const对象的非const数据成员、const数据成员,也可以访问const对象内的所有数据成员;
2)非const成员函数可以访问非const对象的非const数据成员、const数据成员,但不可以访问const对象的任意数据成员;
3)常量对象只能调用常量成员函数;非常量对象可以调用常量成员函数,当const版本和非const版本的成员函数同时出现时,非const对象调用非const成员函数。
4)作为一种良好的编程风格,在声明一个成员函数时,若该成员函数并不对数据成员进行修改操作,应尽可能将该成员函数声明为const 成员函数。
5)引用/指向一个常量,必须使用对常量的引用/指向常量的指针。但对常量的引用/指向常量的指针可以引用/指向非常量。
作者也是一个C++的初学者,文章中难免有错误和不周到的地方,希望大家指正。
本博客参考了很多来自@CTHON https://home.cnblogs.com/u/cthon/的博文内容,十分感谢。