Use const whenever possible
尽可能使用const
const多才多艺。可以修饰全局、局部变量,或修饰文件、函数或者区块作用域中被声明为static的对象。也可以用来修饰class内部的static和non-static成员变量。
const修饰指针
对于指针,const可以修饰指针所指物,也可以修饰指针本身。
char greeting[] = "Hello";
char* p = greeting; // non-const pointer, non-const data
const char* p1 = greeting; // non-const pointer, const data
char* const p2 = greeting; // const pointer, non-const data
const char* const p3 = greeting; // const pointer, const data
记忆方法,当const出现在 * 左侧的时候,表示被指物是常量;当const出现在 * 右侧时,表示的是指针自身是常量。
const修饰函数
- 修饰返回值
class Rational
{
private:
/* data */
public:
Rational(/* args */) {}
~Rational() {}
friend const Rational operator* (const Rational& lhs, const Rational& rhs);
};
虽然不建议返回const对象,但是有时会避免一种不经意间发生的错误。
Rational a, b, c;
if (a * b = c) { // error: passing 'const Rational' as 'this' argument discards qualifiers
// todo
}
- const成员函数
class Fred {
public:
void inspect() const; // This member promises NOT to change *this
void mutate(); // This member function might change *this
};
void userCode(Fred& changeable, const Fred& unchangeable)
{
changeable.inspect(); // Okay: doesn't change a changeable object
changeable.mutate(); // Okay: changes a changeable object
unchangeable.inspect(); // Okay: doesn't change an unchangeable object
unchangeable.mutate(); // ERROR: attempt to change unchangeable object
}
- const-overloading
class Fred { /*...*/ };
class MyFredList {
public:
const Fred& operator[] (unsigned index) const; // Subscript operators often come in pairs
Fred& operator[] (unsigned index); // Subscript operators often come in pairs
// ...
};
很多人忽视了一个C++的重要特性:两个成员函数如果只是常量性(constness)不同,可以被重载。
通常下标运算符重载成对出现。
void f(MyFredList& a) // The MyFredList is non-const
{
// Okay to call methods that inspect (look but not mutate/change) the Fred at a[3]:
Fred x = a[3]; // Doesn't change to the Fred at a[3]: merely makes a copy of that Fred
a[3].inspect(); // Doesn't change to the Fred at a[3]: inspect() const is an inspector-method
// Okay to call methods that DO change the Fred at a[3]:
Fred y;
a[3] = y; // Changes the Fred at a[3]
a[3].mutate(); // Changes the Fred at a[3]: mutate() is a mutator-method
}
void f(const MyFredList& a) // The MyFredList is const
{
// Okay to call methods that DON'T change the Fred at a[3]:
Fred x = a[3];
a[3].inspect();
// Compile-time error (fortunately!) if you try to mutate/change the Fred at a[3]:
Fred y;
a[3] = y; // Fortunately(!) the compiler catches this error at compile-time
a[3].mutate(); // Fortunately(!) the compiler catches this error at compile-time
}
- 物理常量 vs 逻辑常量
物理常量(physical constness)也称为bitwise constness。物理常量的阵营认为,成员函数不更改对象内的任意一个bit。
而不幸的是,很多成员函数并不十足具备const的性质却能通过bitwise的测试。
比如如下代码,我们有一个类型为char*
的成员变量,operator[]不更改pText,于是编译器开心地认为它是bitwise的。
但是,事实上通过以下代码,终究改变了其值。
class TextBlock
{
private:
/* data */
char* pText;
mutable std::size_t textLength;
// std::size_t textLength;
mutable bool lengthIsValid;
// bool lengthIsValid;
public:
TextBlock(/* args */) {}
~TextBlock() {}
std::size_t length() const;
char& operator[](std::size_t position) const { // bitwise声明,但是不适当。
return pText[position];
}
};
std::size_t TextBlock::length() const
{
if (!lengthIsValid) {
textLength = std::strlen(pText);
lengthIsValid = true;
}
return textLength;
}
const TextBlock tb("abcd");
char* pc = & tb[0];
*pc = 'T';
总结
- 将某些东西声明为const可帮助编译器侦测出错误用法,推荐使用。
- 编译器强制实施bitwise constness,但是编程的时候应该使用“逻辑上的常量性”。