目录
对成员函数的const,有两个流行概念:bitwise constness(又称physical constness)和logical constness。
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';
如此,虽然我们设置了一个常量对象cctb
,并且只调用了它的const成员函数,但是终究还是改变了它的值。
认可逻辑上const的人们认为一个const成员函数可以修改它处理的对象的某些bits,但只有在客户端侦测不出(不知道发生了这件事)才可以如此。如下的CTextBlock
类有可能cache文本区块的长度以便应付询问(快速回答):
class CTextBlock {
public:
...
std::size_t length() const;
private:
char* pNext;
std::size_t textLength; // 最近一次计算的文本区块长度
bool lengthIsValid; // textLength是否还有效
};
std::size_t CTextBlock::length() const {
if (!lengthIsValid) {
textLength = std::strlen(pText); // const成员函数内赋值不被允许
lengthIsValid = true; // const成员函数内赋值不被允许
}
return textLength;
}
然而上面的代码不满足物理上的const,编译器不允许,会编译错误。虽然这两个数据的修改对const CTextBlock
对象而言可以接受。解决方法是加mutable
(可变的)关键字,它释放掉非静态成员变量的物理const(bitwise constness)约束:
class CTextBlock {
public:
...
std::size_t length() const;
private:
char* pNext;
mutable std::size_t textLength; // 最近一次计算的文本区块长度
mutable bool lengthIsValid; // textLength是否还有效
};
std::size_t CTextBlock::length() const { ... } // 同上
class TextBlock {
public:
...
const char& operator[](std::size_t position) const {
...
return pText[position];
}
char& operator[](std::size_t position) {
return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);
}
}
“以对象管理资源”(如智能指针)的观念常被称为:RAII(Resource Acquisition Is Initialzation,资源取得时机便是初始化时机)。
RAII对象们在构造函数中获得资源,在析构函数中释放资源。
如:
template <typename T>
class Widget {
...
};
namespace std {
template <typename T>
void swap<Widget<T>>(Widget<T>& a, Widget<T>& b) {
...
}
}
编译错误信息:
error: non-type partial specializtion 'swap<Widget<T> >' is not allowed
会导致可以通过const成员函数修改到对象内部,见条款3的例子。避免返回成员变量的非const引用,帮助const成员函数的行为像个const。
class Point { ... };
struct RectData {
Point ulhc; // upper left-hand corner
Point lrhc; // lower right-hand corner
};
class Rectangle {
public:
...
const Point& upperLeft() const { return pData->ulhc; }
const Point& lowerRight() const { return pData->lrhc; }
...
private:
std::shared_ptr<RectData> pData;
}
class GUIObject { ... };
const Rectangle boundingBox(const GUIObject& obj);
// 用户有可能如下使用:
GUIObject* pgo;
...
const Point* pUpperLeft = &(boundingBox(*pgo).upperLeft());
然而在最后一行语句结束之后,boundingBox函数返回的const Rectangle
临时对象已经被析构。所以这个指针就是一个空悬指针。
不被公开使用的成员函数也就是被声明为protected或private者。如果某成员函数返回不被公开使用的成员函数,那么后者的实际访问级别就会提高至与前者同级。
“pure virtual函数必须在derived classes中重新声明,但它们也可以拥有自己的实现”:通过对一个pure virtual函数一份定义:
class AirPlane {
public:
virtual void fly(const Airport& destination) = 0;
...
};
void AirPlane::fly(const Airport& destination) {
...
}
class ModelA: public AirPlane {
public:
virtual void fly(const Airport& destination) {
AirPlane::fly(destination);
}
...
}
class ModelC: public AirPlane {
public:
virtual void fly(const Airport& destination) {
自己的实现...
}
...
}
当编写一个class template,而它所提供之“与此template相关的”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为“class template”内部的friend函数。
原文:https://www.cnblogs.com/sandychn/p/12452472.html