const一般用来声明只读、不可修改的状态
一、const修饰变量、指针和引用
1、修饰变量
const int a = 20; //则a的值不可被修改
const int a = 20;
a = 30; //编译报错
2、修饰指针变量
int a = 20;
const int *p = &a; 常量指针 指针的指向地址&a可以改,但是指针指向的值20不可以改
int * const p = &a; 指针常量 指针的指向地址不可以改,但是指针指向的值可以改
【记忆方法】
1)const int *p = &a; const 后 跟 *p,则*p不可更改
int a = 20;
int b = 10;
const int* p = &a;
*p = 30; //编译错误
p = &b; //OK
2)int * const p = &a; const 后跟 p,则 p 不可更改
int a = 20;
int b = 10;
int* const p = &a;
*p = 30; //OK
p = &b; //编译错误
3)const int * const p = &a; 指针的指向地址和指针指向的值都不可以修改
int a = 20;
int b = 10;
const int* const p = &a;
*p = 30; //编译错误
p = &b; //编译错误
3、修饰引用
修饰引用的规律和上诉修饰指针大致相同
int a = 20;
const int &b = a; //修饰&p,则b的数值不可修改 b = 30;错误。但是a =30;可以
二、const在传入参数中的应用
const作为函数入参修饰变量、指针和引用 与上诉第 一 节的作用是一样的。见如下代码:
//const修饰常数
void MyFun(const int param)
{
param = 30; //编译错误
}
//const修饰指针
void MyFunA(const int* param)
{
int b = 10;
*param = 30; //编译错误
param = &b; //OK
}
void MyFunB(int* const param)
{
int b = 10;
*param = 30; //OK
param = &b; //编译错误
}
//const修饰引用
void MyFunC(const int& param)
{
int b = 10;
param = 30; //编译错误
}
在日常项目中,经常会使用 const T& param作为某一些方法的传入参数,即如下代码所示。
template<typename T>
void myFun(const T& param)
{
cout << "..." << endl;
}
int main(int argc, char *argv[])
{
int data = 50;
myFun(data);
}
其中T可以看做任意数据类型。传入的param可以是某个变量,也可以是某个对象。
这样的传参具有以下2个特点:
1)引用传入,即传址方式传入,所以调用myFun的时候不会发生参数拷贝,所以也不用浪费内存了;
2)传参使用const修饰,所以myFun函数不允许修改param的值,即data的数据不会因为myFun产生任何修改;
基于这2个特点,如果不想在myFun修改param的值,又想要提高效率,就可以使用这种方式传参。
三、const修饰函数返回值
见如下代码是编译通过的,说明1)const修饰内置类型的返回值,和没修饰是一样的效果。ret能否修改不是取决于myFun的返回值是否是const,而是取决于ret本身是否是const。
const int myFun(int param)
{
return 888;
}
int main(int argc, char *argv[])
{
int param = 50;
int ret = myFun(param); //OK
ret = 999; //OK
}
2)const 修饰自定义类型的作为返回值,此时返回的值不能作为左值使用,既不能被赋值,也不能被修改。
3)const 修饰返回的指针或者引用,是否返回一个指向 const 的指针,取决于我们想让用户干什么。
四、const修饰类成员变量
const修饰成员变量和修饰局部变量是一样的。见下列代码,大家可以复习一下
class MyClassA
{
public:
MyClassA() : _num1(0), _num2(0),_pointer1(nullptr), _pointer2(&_num1){}
public:
int _num1;
const int _num2;
const int* _pointer1;
int* const _pointer2;
};
int main()
{
int a = 6;
MyClassA mc;
mc._num2 = 233; //Error
mc._pointer1 = &a; //Ok _pointer1会指向a
mc._pointer2 = &a; //Error
*mc._pointer1 = 233; //Error
*mc._pointer2 = 233; //Ok _pointer2指向的内存,即mc._num1数值会变为233
system("pause");
return 0;
}
需要注意的是,在MyClass的初始化列表中如果把_pointer2初始化为_pointer2(&_num2)是会报错的,原因是类型不符。
五、const修饰对象
const修饰的对象,则此对象中的任何成员变量都不可以被修改。具体见下列代码
class Base
{
public:
Base() : _value(0){}
void SetValue(int val)
{
_value = val;
}
int GetValue()
{
return _value;
}
int GetValueConst() const
{
return _value;
}
public:
int _value;
};
int main()
{
const Base bs;
bs._value = 233; //Error
bs._value; //Ok 没有作为左值修改
bs.GetValue(); //Error GetValue不是const成员函数,GetValue有修改成员变量的可能,而const修饰的对象要杜绝这种可能
bs.GetValueConst(); //Ok GetValueConst是const成员函数,不可能修改成员变量,所以OK
}
六、const修饰类成员变量
const修饰类成员变量见下列代码的MyFun函数,声明的时候在()后加const
被const声明的成员函数,无法修改任何类成员变量,即在const成员函数中使用的成员变量,可以视为临时被const修饰了。
具体见下列代码
class Base
{
public:
Base() : _value(0){}
void SetValue(int val)
{
_value = val;
}
int GetValue()
{
return _value;
}
int GetValueConst() const
{
return _value;
}
public:
int _value;
};
class MyClass
{
public:
MyClass() :
_num(0),
_pointer(&_num),
_base() {}
void myFun() const
{
int a = 666;
_num = 666; //Error 类成员变量_num不能被修改
_num; //Ok 没有作为左值,没有被修改的风险
_pointer = &a; //Error 类成员变量_pointer不能被修改
*_pointer = 666; //Ok *_pointer不是类成员变量,可以修改
//_base可以视为一个被const修饰的对象,即const Base _base;
_base._value = 233; //Error
_base.SetValue(233); //Error
_base.GetValue(); //Error GetValue不是const成员函数,GetValue有修改成员变量的可能,而_base在myFun() const{} 中不可被修改
_base.GetValueConst(); //Ok
}
public:
int _num;
int* _pointer;
Base _base;
};