1、要求在堆中建立对象:
因为非堆对象在结束时自动调用析构函数,因此其析构函数必须申明为public
。所以只要将类的析构函数声明为private
即可实现这种限制,同时可以引进一个专用的伪析构函数,用来访问真正的析构函数,释放建立的对象。
例:注意看注释
//.h
class UPNumber {
public:
UPNumber();
UPNumber(int init Value);
UPNumber(double init Value);
UPNumber(const UPNumber& rhs);
// 伪析构函数 (一个 const 成员函数, 因为即使是 const 对象也必须能被释放。)
void destroy() const { delete this; } //delete调用私有析构函数
...
private:
~UPNumber();
};
//.cpp
UPNumber n; // 错误! (此步骤合法,但是当它的析构函数被隐式地调用时,就不合法了)
UPNumber *p = new UPNumber; //正确,在堆上建立对象
...
delete p; // 错误! 试图手动调用private 析构函数
p->destroy(); // 正确,通过伪析构函数调用delete(delete会调用析构函数)
2、在堆中建立对象对继承和包含的影响:
a)对继承
class UPNumber { ... }; // 声明析构函数或构造函数为private
class NonNegativeUPNumber:
public UPNumber { ... }; // 错误! 析构函数或构造函数不能编译
解决方法:把析构函数声明为protected(同时它的构造函数还保持public)就可以解决继承的问题
class UPNumber { ... }; // 声明析构函数为 protected
class NonNegativeUPNumber:
public UPNumber { ... }; // 现在正确了; 派生类能够访问protected 成员
注:此时派生类已可以访问基类析构函数,因此派生类对象可以建立在堆上或栈上(基类成分与派生类成分相同)。
b)对包含
class Asset {
private:
UPNumber value; // 错误! 析构函数或构造函数不能编译
...
};
解决方法:修改为包含指向基类对象的指针
//.h
class Asset {
public:
Asset(int init Value);
~Asset();
...
private:
UPNumber *value;
};
//.cpp
Asset::Asset(int init Value)
: value(new UPNumber(init Value)) // 正确
{ ... }
Asset::~Asset()
{ value->destroy(); } // 也正确
注:此时派生类对象中基类部分仍然在堆中,基类析构仍需要通过伪析构函数调用。
总结:
1、通过将析构函数声明为private
可以限制只能在堆上构造对象。
2、当只能在堆上构造对象,析构时需要自定义伪析构函数(因为析构函数是私有)。
3、在伪析构函数中调用delete
(delete会调用析构函数)。
4、伪析构函数需要为const
成员函数(因const 对象也需要被析构)。
5、为使在派生类中可以构造对象,需将基类析构函数声明为protected
。
6、为使在包含类中可以构造对象,需包含成员类的指针。