《More Effective C++》
条款27: 要求或禁止基于堆的对象
有时你想这样管理某些对象,要让某种类型的对象能够自我销毁,也就是能够“delete this”。很明显这种管理方式需要此类型对象被分配在堆中。而其它一些时候你想获得一种保障:“不在堆中分配对象,从而保证某种类型的类不会发生内存泄漏。”如果你在嵌入式系统上工作,就有可能遇到这种情况,发生在嵌入式系统上的内存泄漏是极其严重的,其堆空间是非常珍贵的。有没有可能编写出代码来要求或禁止在基于堆的对象(heap-based object)呢?通常是可以的,不过这种代码也会把“在堆中”的概念搞得比你脑海中所想的要模糊。
要求基于堆的对象
让我们先从必须在堆中建立对象开始说起。为了执行这种限制,你必须找到一种方法禁止以调用“new”以外的其它手段建立对象。这很容易做到。非堆对象(non-heap object)在定义它的地方被自动构造,在生存时间结束时自动被释放,所以只要禁止使用隐式的构造函数和析构函数,就可以实现这种限制。
把这些调用变得不合法的一种最直接的方法是把构造函数和析构函数声明为private。这样做副作用太大。没有理由让这两个函数都是private。最好让析构函数成为private,让构造函数成为public。处理过程与条款26相似,你可以引进一个专用的伪析构函数,用来访问真正的析构函数。客户端调用伪析构函数释放他们建立的对象。
例如,如果我们想仅仅在堆中建立代表无限精度数字的对象,可以这样做:
class UPNumber {
public:
UPNumber();
UPNumber(int initValue);
UPNumber(double initValue);
UPNumber(const UPNumber& rhs);
// 伪析构函数(一个const 成员函数, 因为
// 即使是const对象也能被释放)
void destroy() const { delete this; }
...
private:
~UPNumber();
};
然后客户端这样进行程序设计:
UPNumber n; // 错误!(在这里合法,但是
// 当它的析构函数被隐式地
// 调用时,就不合法了)
UPNumber *p = new UPNumber; // 正确
...
delete p; // 错误!试图调用
// private 析构函数
p->destroy(); // 正确