场景一 用于修饰指针

char greeting[] = "Hello";
char* p = greeting; // non-const pointer, non-const data
const char* p = greeting; // non-const pointer, const data
char* const p = greeting; // const pointer, non-const data
const char* const = greeting; // const pointer, const data

场景二 用于对象前、后

void f1(const Widget* pw);
void f2(Widget const * pw);

场景三 用于对STL迭代器的修饰

std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin(); // iter的作用相当于 T* const
*iter = 10; // 没问题,改变iter所指物
iter++; // 错误,iter是const
std::vector<int>::const_iterator cIter = vec.begin(); // cIter的作用相当于 const T*
*cIter = 10; // 错误,*cIter是个const
++cIter; // 没问题,改变cIter

场景四 用于返回值

class Rational {...};
const Rational operator* (const Rational& lhs, const Rational& rhs);
Rational a, b, c;
...
(a * b) = c; // 显然在把返回结果设置为const以后,就不允许这样的操作发生
if (a * b = c)  // 如果设置返回值为const的时候,这种手误的操作也不会发生

场景五 用于成员函数本体

const成员函数
class TextBlock {
public:
'''
const char& operator[](std::size_t position) const // operator[] for const对象
{return text[position];}
char& operator[](std::size_t position)
{return text[position];]}
private:
std::string text;
}
// 调用一
TextBlock tb("Hello");
std::cout << tb[0]; // 调用non-const TextBlock::operator[]
const TextBlock ctb("World");
std::cout << ctb[0]; // 调用const TextBlock::operator[]
// 调用二
void print(const TextBlock& ctb) {
std::cout << ctb[0]; // 调用const TextBlock::operator[]
...
}
// 调用三
std::cout << tb[0]; //没问题,读一个non-const TextBlock
tb[0] = 'x'; //没问题,写一个non-const TextBlock
std::cout << ctb[0]; //没问题, 读一个const TextBlock
ctb[0] = 'x'; //错误,写一个const TextBlock, 错误的原因在于operator[]的返回值为const

场景六 bitwise constness和logical constness

class CTextBlock {
public:
...
char& operator[](std::size_t position) const //bitwise const 声明,但其实不适当
private:
char* pText;
}
// 调用
const CTextBlock cctb("Hello"); // 声明一个常量对象
char* pc = &cctb[0]; // 调用const operator[]取得一个指针,指向cctb的数据
*pc = 'J'; // cctb现在有了"Jello"这样的内容
// 说明
// const 修饰函数体,说明函数体内不能修改任何non-static成员变量,在函数题内却是没有修改成员变量,但是最后还是修改成功了,那是因为返回值不是char;bitwise constness的主张是成员变量一个bit都不能修改,以上情况导出所谓的logical constness。
class CTextBlock {
public:
...
std::size_t length() const;
private:
char* pText;
std::size_t textLength; //最近一次计算的文本区块长度
bool lengthIsValid; //目前的长度是否有效
};
// 成员函数实现
std::size_t length() const {
if (!lengthIsValid) {
textLength = std::strlen(pText); //错误,在const成员函数内不能赋值给textLength和lengthIsValid
lengthValid = true;
}
return textLength;
}

场景七 mutable用法

// 用mutable(可变的)释放掉non-static成员变量的bitwise constness约束
class CTextBlock {
public:
...
std::size_t length() const;
private:
char* pText;
mutable std::size_t textLength; //这些成员变量可能总是被更改,即使在const成员函数内。
mutable bool lengthIsValid;
};
std::size_t CTextBlock::length() const {
if (!lengthIsValid) {
textLength = std::strlen(pText);
lengthIsValid = true;
}
return textLength;
}

场景八 const实现non-const成员函数

在const和non-const成员函数中避免重复
class TextBlock {
public:
...
const char& operator[](std::size_t position) const {
... // 边界检查
... // 日志记录数据
... // 检验数据完整性
return text[position];
}
char& operator[](std::size_t position) {
... // 边界检查
... // 日志记录数据
... // 检验数据完整性
return text[position];
}
private:
std::string text;
};
// 可以看到以上有非常严重的代码重复, 改进就是用const operator[] 实现 non-const operator[]
class TextBlock {
public:
...
const char& operator[](std::size_t position) const {
...
...
...
return text[position];
}
char& operator[](std::size_t position) {
return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);
// const_cast转型是因为返回类型,static_cast转型是为了转成const对象。
}
};

总结

  1. 将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。
  2. 编译器强制实施bitwise constness,但你编写程序时应该使用"概念上的常量性(conceptual constness)"。
  3. 当const和non-const 成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复.
05-11 22:33