目录
1. const自白
如果某个对象的值应该是保持不变(事实),你应该要把它说出,而我(const)就是帮你干这个事的。我可以告诉编译器和其他程序员该值保持不变,而编译器也会强制实施这项约束。
2. 指向常量和常指针
const在*号左边表示指向常量,const在 *号右边表示常指针。
char greetting[] = "Hello";
char* p = greetting;//指向非常量,非常指针
const char* p = greetting;//指向常量,非常指针
char* const p = greetting;//指向非常量,常指针
const char* const p = greetting;//指向常量,常指针
下面两种写法是一样的
void f(const Widget* w);
void f(Widget* const w);
3. const修饰的STL迭代器
声明迭代器为const,即声明一个T* const 指针;const_iterator是一个指向常量的指针。
std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin();
*iter = 10;//没问题,改变iter所指的对象
iter++;//错误,iter为常指针
std::vector<int>::const_iterator cIter = vec.begin();
*cIter = 10;//错误,*cIter是一个常量
cIter++;//没问题,改变cIter的值
4. const最威用法
4.1 返回常数值
避免用户错误使用左值,又不失安全性和高效性。
class Rational{
...
const Rational operator* (const Rational& lhs, const Rational& rhs);
}
Rational a,b,c;
(a * b) = c;//错误用法,编译器报错,有助于查错
if( (a * b) = c) ...//错误用法,编译器报错,有助于查错
4.2 const成员函数
const成员函数,不能修改对象的数据成员,用const修饰成员函数,本质上是修饰隐藏的this指针。const成员函数和non-const成员函数是可以重载的。基于以下两方面,程序员应当尽量严谨地考虑是否用const修饰成员函数。
- 有利于理解class接口;
- 可以用来处理const对象;
class TextBlock{
...
const char& operator[] (std::size_t position) const{
return text[position];
}
char& operator[] (std::size_t position) {
return text[position];
}
private:
std::string text;
int length;
}
//const对象大多用于pass-by-reference-to-const或是pass-by-pointer-to-const;
void print(const TextBlock& ctb){
for(int i = 0; i < ctb.length; i++)
std::cout<<ctb[i];
}
TextBlock tb("Hello");
const TextBlock ctb("World"):
std::cout<<tb[0];//没问题,调用char& operator[] (std::size_t position);
tb[0] = 'h';//没问题,因为返回类型为char&,如果是char则错误,而且return-by-value无法改变对象的数据成员。
print(ctb);//没问题,调用const char& operator[] (std::size_t position) const;
ctb[0] = 'w';//错误,因为返回类型为const char&,不能改变对象的数据成员。
4.3 bitwise const阵营 VS logical const阵营
bitwise const:常成员函数不可以更改no-static的成员变量;但存在缺陷,如下代码所示:
class CTextBlock{
public:
...
char& operator[] (std::size_t position) const {
return pText[position];
}
private:
char* pText;
};
const CTextBlock cctb("Hello");
char* pc = &cctb[0];
*pc = 'J';//通过[]操作符和指针更改了成员变量的值
另外在有些情况下,我们希望在客户端侦测不出来的情况下,常成员函数可以改变成员变量的值,但编译器坚持bitwise const;此时,我么可以使用mutable释放非静态成员的bitwise const约束。
class CTextBlock{
public:
...
std::size_t lenght() const;
private:
char* pText;
std::size_t textLength;
bool lengthIsValid;
};
std::size_t CTextBlock::length() const{
if(!lengthIsValid){
textLength = std::strlen(pText);//编译不通过
lengthIsValid = true;//编译不通过
}
return textLength;
}
//使用mutable
class CTextBlock{
public:
...
std::size_t lenght() const;
private:
char* pText;
mutable std::size_t textLength;
mutable bool lengthIsValid;
};
std::size_t CTextBlock::length() const{
if(!lengthIsValid){
textLength = std::strlen(pText);//编译通过
lengthIsValid = true;//编译通过
}
return textLength;
}
4.4 casting转型使得代码复用
class CTextBlock{
public:
...
char& operator[] (std::size_t position) const {
...//相关复杂的操作
...//如边界检查,标志数据访问等等
return text[position];
}
char& operator[] (std::size_t position) {
...//相关复杂的操作
...//如边界检查,标志数据访问等等
return text[position];
}
private:
std::String text;
};
//调用操作符[] const并使用转型操作,避免代码重复
class CTextBlock{
public:
...
char& operator[] (std::size_t position) const {
...//相关复杂的操作
...//如边界检查,标志数据访问等等
return text[position];
}
char& operator[] (std::size_t position) {
return
const_cast<char&>(
static_cast<const CTextBlock&>(*this)
[position]
);
}
private:
std::String text;
};
Reference
[1] Scott Meyers. 电子工业出版社. Effective C++中文版[M]. 2006.