const是C++的精髓。用法很多,学会善用const非常非常重要。
const变量
const可用于修饰变量,表示这个变量不可修改:
const double pi = 3.14;
// double const pi = 3.14; 等效的(const放在前后都可以)
这样一来,x
就成为编译器眼里的常量,如果尝试给它赋值编译器就报错。这样后面用到很多次需要修改时,就只用改一个了。它的语义通常是“这个变量是整个程序的全局属性,放在开头方便修改”。
喜欢违背规则是人的本性。如果我非要修改它怎么办?那我可以把他的地址取出来强行修改:
const int a = 3; // 1
main() {
static const int a = 3; // 2
const int a = 3; // 3
int *p = (int *)(&a); //获取a的地址,强行转换修改
cout << *p << endl;
*p = 9;
cout << "modifyed\n";
cout << *p << endl;
}
测试发现,情况1和2会报错,3则正常运行。这是因为1和2中a定义为const后编译时就被放到了只读区,在运行时无权限修改;而情况3下a成为一个栈变量,所以运行时跟其他变量没区别,不会有限制,仅仅是编译器不让你直接改而已。
以上这些都是作死取常量地址导致的,如果没有取地址又开了些优化的话,编译时有可能并不生成a,而是像define一样直接替换后文。这样时间上会更快,但空间上往往会更大。
const指针与传参
下面再看看const作用到指针和引用的情况
int n = 3;
const int cn = 3;
const int &nn = n;
// int &nn = cn;
int *const p = &n;
const int *p = &n;
引用很简单,跟普通变量没有区别。nn不能赋值(但可以给n赋值一起改变两个),但cn不能绑定给nn,因为const赋值给别人时只能继续加不能消除,不然一个引用就可以去掉常量属性了。
在看指针,指针的*好像有很多拜访方式,但其实就两类:
- const在*左侧,此时p可以修改,但*p不能修改
- const在*右侧,此时*p可以修改,但p不能修改,和引用一样,const数不能赋值给这种能修改的指针
多重指针const的通用规则
以二级指针为例,对于多重指针来说也一样。
const与成员函数
const的另一个重要用途就是修饰成员函数。给一个函数加上const,表示该对象成员不可被修改:
struct A{
int a;
void foo() const {
//a = 6; 错误!不可修改
}
};
这种方式好处多多,它的语义即“该函数不修改对象”,让编译器来检查,也方便阅读。
const成员函数的重载
在C++中,两个成员函数如果一个有const另一个没有,那么他们是可以重载的。
class TextBlock{
public:
const char& operator[](int pos) const {return text[pos];}
char& operator[](int pos) {return text[pos];}
};
这个重载的意义是什么呢?因为const对象只能调用const函数,所以这样一来,const对象会调用第一个(仅能读),其他对象会调用第二个(支持读写)。
constexpr(C++11)
constexpr
关键字是C++11新加入了关键字。用于在某些情况下替代const。在传统C++里,const修饰的量分为两种:编译时常量和运行时常量:
const int a = 5;//编译时常量
void f(int t) {
const int p = t; //运行时常量
}
显然,编译时常量就是可以在编译器确定值的,反之则是直到运行才能确认,对他们的处理策略是大不相同的。但是有些时候不好判断你的const是那一种,所以constexpr就成为了手动区分他们的工具。
上例中,将a修改为constexpr,通过编译,验证说明a是编译时常量(因为5是);修改b时则报错,说明b不是编译时常量(因为t不是)。
constexpr还可作用给函数:
constexpr int size() {
return 42;
}
constexpr int scale5(int x) {
return 5 * x;
}
constexpr int a = scale5(4);
constexpr int b = size();
// constexpr int c = scale5(i);
这样一个函数必须要短小(只能有一条return语句)。如果参数为常量表达式(scale5(4)
),那么返回值也为常量。否则会被当做一般函数处理,所以c的赋值是错误的,即constexpr函数不一定返回常量值。
注:这是C++11中的constexpr内容,在后续版本中,constexpr的用法又有了较大的修改。
const会提高效率吗
读一个只读变量和读写变量,没有理由存在效率上的差异。但是在编译的层面上,有意添加const可以为编译器提供更多信息,指示编译器更加大胆的优化(就好比volatile
或者restrict
一样),所以在特定情况下可以生成更优秀的汇编码,跑得更快。