const修饰函数的三种形式
const int& fun(int& a)//修饰返回值
int& fun(const int& a)//修饰形参
int& fun(int& a)const{} //const成员函数
1、const返回值
多是修饰返回值是引用类型,避免返回值成为左值被修改。如果函数返回类型是内置类型,则修改返回值本身就不合法,所以const返回值是处理返回类型为用户定义类型情况。
ps.返回值为左值的情况,例如手误 if(a*b=c),所以重载*时返回值应为const避免这种情况
2、const修饰参数
构造接口时要思考函数是否修改参数,如果不修改,加上const
3、const成员函数
const修饰成员函数,根本上是修饰this指针
class A {
public:
void func()const {}
void func() {}
};
func()const {}, this指针是const A* const this; this指针是const,this所指也是const
func() {}, 与上面是重载函数,是两个函数,因为形参不同(this指针不同),this指针A* const this; this指针是const,this所指可以修改
【注】如果成员函数同时具有const和non-const版本,const对象只能调用const成员函数,non-const对象只能调用non-const成员函数
1、const对象必须初始化
2、多个文件之间共享const对象,必须在变量定义前添加extern
3、const的引用
const int ci=1024;
const int &r1=ci; //正确
int &r2=ci; //错误,试图让一个非常量引用指向常量对象
4、引用的类型要与绑定的对象严格匹配,且不能与字面值或表达式计算结果绑定在一起
double d1 = 3.14;
int& r1 = d1; //错误
double i = 10;
const int& r2 = i; //true
r2真正绑定的是一个临时量,若r2不是const则可以对r2绑定的对象进行修改,但r2并不绑定i,故无法修改i的值,故第一个引用是错误的
两种例外
(1)初始化常量引用允许用任意表达式作为初始值,只要该表达式结果可以转换成引用类型,且允许常量引用绑定非常量的对象、字面值、一般表达式
int i = 10;
const int& r1 = i; //true
const int& r2 = 11; //true
const int& r3 = r1 * 2; //true;
int &r4= r1 * 2; //false,r4是非常量引用
(2)可以将基类的指针或引用绑定到派生类对象上
5、指针与const
(1)指向常量的指针
指向常量的指针不能用于改变其所指对象的值。要想存放常量对象的地址,只能使用指向常量的指针
const double d1 = 3.14;
double *ptr = &d1; //false
const double* ptr1 = &d1; //true
*ptr1 = 4.14; //false
允许一个指向常量的指针指向一个非常量对象
double d1 = 3.14;
const double* ptr1 = &d1; //true,但是不能用ptr1改变d1的值
(2)const 指针 能否通过指针修改其所指对象的值完全依赖于所指对象的类型
6、顶层const
顶层const表示指针本身是个常量,底层const表示指针所指对象是个常量
顶层const可表示任意的对象是常量,对任何数据类型适用;底层const与指针和引用等复合类型有关。指针类型既可以是顶层const又可以是底层const
执行拷贝操作时,顶层const与底层const区别明显:顶层const不受影响,但拷入和拷出的对象必须具有相同的底层const或两个对象的数据类型可以转换
int i = 0;
const int ci = 42; //顶层const,不能改变ci的值
const int *p2 = &ci; //底层const,允许改变p2的值
const int *const p3 = p2; //靠右顶层,靠左底层
i = ci; //正确,顶层const无影响
p2 = p3; //正确,p2和p3指向的对象类型相同。顶层const无影响
int* p = p3; //错误,p3有底层const但p没有
p2 = &i; //正确
int& r = ci; //错误
const int& r2 = i; //正确