c++中const关键字
关于const关键字,有几个注意点。
const的引用
首先是把引用绑定到const对象上,与普通引用不同,对常量的引用不能被用来修改它所绑定的对象。
const int ci=1024;
const int &r1=ci; //正确:引用及其对应的对象都是常量
r1=42; //错误:r1是对常量的引用,不可修改
int &r2=ci; //错误:ci是常量,需要常量引用
对于非常量对象、字面值、一般表达式,也允许用常量引用绑定,但此时不可通过修改常量引用来修改原非常量对象。
int i=42;
const int &r1=i; //正确:允许将const int&绑定到非const对象上
const int &r2=42; //正确:42是字面值,r2是常量引用
const int &r3=r1*2; //正确:r1*2是一般表达式,r3是常量引用
int &r4=r1*2; //错误:r1*2是一般表达式,r4是非常量引用
r1=1; //错误:r1是常量引用,不可修改r1
i=1; //正确:i不是常量
也就是说,常量引用只是对引用可参与的操作做出了限定,而对于引用的对象本身是否是const未作限定。
指针和const
关于指针和const,可以分为指向常量的指针和常量指针。
对于指向常量的指针,可以类比常量引用,不能通过该指针修改所指对象的值,对于所指对象是否是常量也未作限定。
const int i=42;
int *p1=&i; //错误:i是常量,p1是普通指针
const int *pi=&i; //正确:i是常量,pi是指向常量的指针
*pi=1; //错误:不能给*pi赋值
int j=42;
const *pj=&j; //正确:j不是常量,pj是指向常量的指针
*pj=1; //错误:不能通过给*pj赋值来修改j的值
j=1; //正确:j不是常量
对于常量指针,在定义时必须初始化,以后不可修改常量指针的值,即不能将该指针指向其他对象。
int i=0,j=0;
int *const pi=&i; //正确:pi是常量指针
*pi=1; //正确:可以给*pi赋值来修改i的值
pi=&j; //错误:pi是常量指针,不能给pi赋值
double pi=3.14,x=0.0;
const double *const pip=π //正确:pip是指向常量的常量指针
*pip=1.1; //错误:不能给*pip赋值来修改pi的值
pip=&x; //错误:不能给pip赋值来将pip指向x
其实,指向常量的常量指针和常量引用才是最为相似的。对于常量引用来讲,我们不能通过给引用赋值来修改所绑定的对象,而引用本身在定义时就必须初始化,之后便不能修改引用(不能将引用绑定到其他对象上)。这两个性质与指向常量的常量指针极为相似,就如上文const double *const pip=π
我们不能通过pip来修改pi的值(给*pip赋值),也不能将pip指向其他对象(给pip赋值)。深入学习的话,是因为c++的引用本身就是由指针来实现的,只不过对外隐藏了自身的值。
顶层const、底层const
介绍完上面的内容,对于顶层const、底层const也就很好理解了。因为指针本身是否是常量和指针所指的对象是否是常量是两个相互独立的问题,所以c++用顶层const表示指针本身是常量,而用底层const表示指针所指的对象是常量。更一般的是,顶层const可以表示任意对象是常量。
int i=1;
int *const p1=&i; //p1是顶层const
const int ci=1; //ci是顶层const
const int *p2=&ci; //p2是底层const
const和类成员
const除了修饰一般变量,还可以修饰类中的成员,可以分为常成员函数和常数据成员。
使用const修饰的函数为常成员函数,声明的格式为:
类型说明符 函数名(参数表) const;
常成员函数有以下特点:
- 常成员函数不能更新目的对象的数据成员。
- 如果一个类的对象是常对象,那么通过该常对象只能调用它的常成员函数,不能调用其他成员函数。
- const可以用于对重载函数的区分。
void print(); void print() const;
非const的对象调用该函数,两个重载的函数都可以匹配,但编译器会选择不带const的函数。
当类的数据成员是常量时,构造函数对该数据成员进行初始化,就只能通过初始化列表。