上接: Effective C++解析 Item3:尽量使用const
(续)
const 修饰成员函数
const修饰成员函数的具体含义:
有两种认同观点:
1. 物理常量性:当且仅当函数不修改任何成员数据时,可以使用const修饰成员函数。
2. 逻辑常量性:常成员函数仅可以在使用者未察觉的情况下修改成员数据。
逻辑常量性:一个成员函数在逻辑上是const,但它却仍需要改变某个成员的值。对于用户而言,这个函数看似没有改变其对象的状态,然而,它却可能更新了某些用户不能直接访问的细节。这通常被称为逻辑的常量性。
比如想在CTextBlock这个类中添加const 成员函数:
class CTextBlock {
public:
...
std::size_t length() const;
private:
char *pText;
std::size_t textLength; // these data members can not
bool lengthIsValid; // be modified
};
std::size_t CTextBlock::length() const
{
if (!lengthIsValid) {
textLength = std::strlen(pText); // Error!
lengthIsValid = true; // error!
}
return textLength;
}
在length()成员函数中需要修改一些标记数据,或应该修改的数据,在这个例子中,需要在length调用时得到默认的文本长度,所以应该在length中修改成员数据。
但是因为const 修饰原因,在函数中直接进行改动会发生错误。
一种改变方法是在const函数中将this对象指针强制转换:
...
...
std::size_t CTextBlock::length() const
{
if (!lengthIsValid) {
CTextBlock * th = const_cast<CTextBlock*>(this); // const cast
th->textLength = std::strlen(pText); // Error!
th->lengthIsValid = true; // error!
}
return textLength;
}
在const函数中认为,对象的this指针是const 型对象指针,所以转换为类型指针。但是尽量不要使用const_cast进行转换。
另外一种方法,就是将需要修改的数据使用mutable修饰。
mutable特别说明这个成员需要以一种能允许更新的方式存储——即使它是某个const对象的成员。
上面的代码可以改写为:
class CTextBlock {
public:
...
std::size_t length() const;
private:
char *pText;
mutable std::size_t textLength; // these data members may
mutable bool lengthIsValid; // always be modified, even in const member functions
};
std::size_t CTextBlock::length() const
{
if (!lengthIsValid) {
textLength = std::strlen(pText); // now fine
lengthIsValid = true; // also fine
}
return textLength;
}