Use const whenever possible.
"只要某值保持不变是事实,就该说出来以获得编译器的帮助" ----- 《Effective C++》
const修饰对象
可修饰global或namespace作用域下的常量,或是文件内、函数内、block内中的对象。
const修饰内置类型变量
- 修饰内置类型时const在类型左右是一样的,代表被修饰对象在生命周期内不可改变。
- 直接赋值const对象会报错,但可以通过取地址强转指针类型再修改的方式绕过这个限制,但这是一种UB,不可为之。
既可以修饰指针,也可以修饰引用,且可以指定指针即其所指物是(不是)可变的。
const修饰指针
常量指针(指向常量的指针,被指物不可变)
const出现在 * 左边(常量const, 指针*),表示被指物是不可变的,会有两种写法,效果相同
const Widget* p
Widget const * p
指针常量(指针是常量,指针不可变)
const出现在 * 右边 指针是不可变的,但所指对象可变。
指针和被指物均为常量
const Widget* const p表示指针和所指对象均不可变
const作用于函数
const修饰形参
const修饰形参代表这个函数内不可对该参数进行修改
const修饰返回值
指定返回值为const,可以避免一些不必要的错误。防止使用者的错误使用造成意外。 例如返回自定义类型对象,调用方可能无意间进行错误的操作(* 解引用, 对自定义类对象进行+-等)
const修饰类成员函数
可以在类成员函数末尾使用const修饰,即void Foo::GetData() const;被const修饰的成员函数中不可修改类成员变量。
有两个作用:
- 表明这个函数中不会修改对象成员。
- 用于操作const对象。因为很多时候需要pass by reference-to-const, 因此我们需要有const成员函数来处理const对象。
const修饰成员函数的原理
我们知道,类成员函数都有一个隐含的this指针
void Foo::GetData(Foo* this)
const修饰成员函数,实际上是修饰隐含的this指针所指的对象
void Foo::GetData(const Foo* this) const
此外要注意的是,const成员函数和non-const成员函数是可以重载的。
- const对象只能调用const成员函数,不能调用non-const成员函数。这是因为const对象调用成员函数时传入的为const类型的this指针
const A* this, 不符合non-const成员函数的参数类型A* this - 同样,const成员函数不能调用non-const成员函数。
- non-const对象/non-const成员函数可以调用const成员函数。
编译器进行的是bitwise constness检查,因此const成员函数不可修改任何non-static成员变量。 与之相对的logical constness概念认为可以在客户感知不到的情况下修改对象的某些bits,这可以通过mutable关键字来实现。通过将成员变量声明为mutable,来允许const成员函数中对它进行修改(移除bitwise constness约束)。
在const 和 non-const版本的成员函数中避免重复
我们已知const和non-const的成员函数是可以重载的,为了避免代码重复,通常可以让non-const来调用const版本的成员函数。
class TextBook {
public:
// const version
const char& operator[](std::size_t position) const {
// 边界检查等操作
return text[position];
}
// non-const version
char& operator[](std::size_t position) {
return const_cast<char&>(static_cast<const TextBook&>(*this)[position]);
// 2.移除constness 1. cast to const reference to call const operator[]
}
private:
std::string text;
};
上面的non-const版本的成员函数调用了const版本,避免了代码重复。 但是要做到这一步,需要进行两步转换:
- 将当前对象通过static_cast转化为const类型对象
static_cast<const TextBook&>*(*this)来调用const版本的函数。 - 将调用const版本函数得到的const char&通过const_cast移除constness,
const_cast<char&>*()
实现原理
从实现上讲,const是一个编译期的语言功能,进行常量替换和赋值限制,在运行时不会对内存有限制。
但是,某些变量位于只读的内存页中,此时使用const_cast来尝试赋值会造成运行时崩溃。
具体来说,对于函数内的const变量,存放于该函数的帧中,程序拥有读写权限。
对于全局的const变量,例如static const char const data[16] = "const data";会存放于全局常量区,程序无读写权限。