C语言中,可以使用#define
定义常量,编译器在预处理阶段直接将宏替换,在C++中,使用const
关键字来定义常量,如常量字符串等。常量存放在常量区(常量区只读不可写)。const 常量在运行时被操作系统载入常量区,属于运行时常量。
C++
中 const
有以下几种用法:const
对象、常量引用、常量指针和const
成员函数。下面从这4个方面介绍 const
关键字。
使用const
对象时,在类中必须定义 const
成员函数,const
对象只能调用 const
成员函数。
class Test_const{
public:
int getVar(){
return var;
}
int getVar() const{
cout << "int getVar() const" << endl;
return var;
}
private:
int var;
};
int main(){
const Test_const ct = Test_const();
cout << ct.getVar() << endl;
return 0;
}
编译执行,const
对象调用 const
成员函数,输出如下。如果类中不定义const
成员函数,则会编译失败。
int getVar() const
0
函数参数经常会使用常量引用来作为形参,使用常量引用的好处是1. 不可修改传入参数 2. 引用传递(值传递与引用传递的区别略)。特别是对于变量占用空间巨大的情况下,使用常量引用,会显著提高程序执行效率。
ReturnType func(const ClassName& param){
// ...
}
对于基本类型,如
int
、double
等,可以直接使用const int/double
。
最令人混淆的就是常量指针、指针常量,这里推荐《C++ Primer》的理解方式,从右向左进行推导其含义。
const int* cpV1; // pointer to const int , 指向常量的地址,值不可变
int const* cpV2; // pointer to const int , 同上
int* const pcV; // const pointer to int , 指向int类型的指针常量,地址不可修改
测试程序及输出结果如下
void constPointer(){
const int i = 10;
const int* pci = &i;
const int j = 20;
pci = &j; // 指针可修改
cout << *pci<< endl; // 20
int const* pci1 = &i;
pci1 = &j;
cout << *pci1 << endl; // 20
int k = 99;
int * const cpi = &k;
*cpi = 9; // 指针指向的值可修改
cout << *cpi << endl; // 9
}
引用与指针的区别
简单来说,指针是指向变量的内存地址,而引用是变量别名。指针可以为空,而引用不为空,必须进行初始化。更进一步来说,在C++
中,编译器将指针常量(constant pointer, 不可改变指向的指针)作为引用的内部实现,即TypeName& var等价于 TypeName* const var,在编译时,所有的var
会被编译为*var
,所以引用会修改原本输入参数的值。
首先思考一个问题,const
变量是否可以修改?下面给出两个示例程序,尝试体会一下const
变量。
示例程序1:修改局部const
变量
int main(){
int j = 6;
cout << &j << endl; // 0x61ff08
const int var = 10;
cout << &var << endl; // 0x61ff04
int* pvar = (int*)(&var);
*pvar = 99;
cout << pvar << endl; // 0x61ff04
cout << *pvar << endl; // 99
}
示例程序2:全局变量 const
const int var = 10;
int main(){
int j = 6;
cout << &j << endl; // 0x61ff08
cout << &var << endl; // 0x40506c
int* pvar = (int*)(&var);
*pvar = 99; // Process finished with exit code -1073741819 (0xC0000005)
cout << pvar << endl;
cout << *pvar << endl;
return 0;
}
示例程序3:全局变量 const volatile
const volatile int var = 10;
int g_i = 11;
int main(){
int j = 6;
cout << &j << endl; // 0x61ff08
cout << &var << endl; // 1, var为volatile int 类型,operator<<并没有重载,所以需要强制转换为(int*)类型输出
cout << (int*)&var << endl; // 0x404004
cout << &g_i << endl; // 0x404008
int* pvar = (int*)(&var);
*pvar = 99;
cout << pvar << endl; // 0x404004
cout << *pvar << endl; // 99
reutrn 0;
}
示例1可以运行成功,其中const
局部变量与普通变量一样,存放在栈区,可以通过地址修改栈区的值。而示例2在运行时直接退出,编译结果如下
0x61ff08
0x40506c
Process finished with exit code -1073741819 (0xC0000005)
可以看到全局区的 const
兑现与普通的栈区地址不同,其存放在常量区,该内存区域的值只读不可修改。所以,当进行修改操作时,程序出错退出。示例3使用 volatile
关键词修饰 const
对象, 其地址与全局变量g_i的地址相邻,其存放在静态区(全局变量区),所以可以通过地址进行修改。
volatile
关键字的作用:指示编译器该变量是可变的,对该变量无需进行优化。
mutable
关键字含义是可变的,与const
关键字相反,只能用来修饰类的成员变量。其作用是,修改 mutable
修饰的变量,并不影响对象的值。如示例程序4所示:
#include
using namespace std;
class Test{
public:
Test():m_count(0), m_i(10){};
int getI() const{
m_count++; // const 成员函数中修改类成员变量,因为该变量被 mutable 修饰
return m_i;
}
int callingTimes() const{
return m_count;
}
private:
int m_i;
mutable int m_count;
};
int main(){
Test t = Test();
cout << t.getI() << endl;
cout << t.getI() << endl;
cout << t.getI() << endl;
cout << t.getI() << endl;
cout << t.getI() << endl;
cout << "t.getI() called times: " << t.callingTimes() << endl;
return 0;
}
mutable
的使用场景:
mutex
保证线程安全
一键三连是对我的支持与鼓励!欢迎关注编程小镇,每天学一点新姿势。