T* const 表示不可指向不同的东西 但指向的东西的值可以改变
const T* 表示不可改变指向的东西的值 但可以指向不同的东西
T* const p (const修饰p p为指针 被const修饰后不该被改动 指针p不能改变 但指向的内容*p可被改变)
const T* p (const修饰*p *p为不该被改动的对象)
STL迭代器:
std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin(); //iter的作用像个T* const
*iter = 10; //可以实现,改变的是所指物
++iter; //错误!iter为const
std::vector<int>::const_iterator cIter= vec.begin(); //cIter的作用像个const T*
*cIter = 10; //错误!*cIter为const
cIter++; //可以改变cIter
const成员函数
两个成员函数如果只是常量性(constness)不同,可以被重载。
logical constness,主张一个const成员函数可以修改他所处理的对象内的某些bits,但只有在客户端侦测不出的情况下才得如此。例如CTextBlock class有可能高速缓存(cache)文本区块的长度以便应付询问:
class CTextBlock {
public:
std::size_t length() const;
private:
char* pText;
std::size_t textLength; //最近一次计算的文本区块长度
bool lengthIsValid; //目前的长度是否有效
};
std::size_t CTextBlock::length() const{
if (!lengthIsValid){
textLength = std::strlen(pText); //错误!在const成员函数内不能赋值
lengthIsValid = true; // 给textLength和lengthIsValid
}
return textLength;
}
解决办法:利用C++的一个与const相关的摆动场:mutable(可变的)。mutable释放掉non-static成员变量的bitwise constness约束:
class CTextBlock {
public:
std::size_t length() const;
private:
char* pText;
mutable std::size_t textLength; //这些成员变量可能总是会被改变
mutable bool lengthIsValid; //即使在const成员函数内
};
std::size_t CTextBlock::length() const{
if (!lengthIsValid){
textLength = std::strlen(pText); //现在这样就可以了
lengthIsValid = true; //
}
return textLength;
}
在const和non-const成员函数中避免重复
class TextBlock{
public:
const char& operator[](std::size_t position) const{
//边界检查(bounds checking)
//志记数据访问(log access data)
//检验数据完整性(verify data integrity)
return text[position];
}
char& operator[](std::size_t position){
//边界检查(bounds checking)
//志记数据访问(log access data)
//检验数据完整性(verify data integrity)
return text[position];
}
private:
std::string text;
}
以上有代码重复以及伴随的编译时间、维护、代码膨胀等问题。真正该做的是实现operator[]的机能一次并使用它两次。即在其中一个调用另一个。将常量性转除(casting away constness)
class TextBlock{
public:
const char& operator[](std::size_t position) const{
//边界检查(bounds checking)
//志记数据访问(log access data)
//检验数据完整性(verify data integrity)
return text[position];
}
char& operator[](std::size_t position){ //现在只调用const op[]
//边界检查(bounds checking)
//志记数据访问(log access data)
//检验数据完整性(verify data integrity)
return
const_cast<char&>( //将op[]返回值的const转除
static_cast<const TextBlock&>(*this) //为*this加上const
[position] ); //调用const op[]
}
private:
std::string text;
}
这份代码有两个转型动作。在non-const operator[]内部若只是单纯调用operator[],会递归调用自己。所以必须明确的指出调用的是const operator[],但C++缺乏直接的语法可以这样做。因此这里将*this从原始类型TextBlock&转型为const TextBlock&。所以这里有两次转型:第一次用来为*this添加const(这使接下来调用operator[]时得以调用const版本),第二次从const operator[]的返回值中移除const。
反向做法(const成员函数调用non-const成员函数)是错误行为:因为对象有可能因此被改动。
- 将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。
- 编译器强制实施bitwise constness,但编写程序时应该使用“概念上的常量性”(conceptual constness)。
- 当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。