因为 C++ 是静态绑定语言。在编译过程中,所有的非虚函数调用都必须被解析完成。即使是虚函数,也需检查可访问性。
当在栈上生成对象时,对象会自动析构,此时对析构函数的需求是必须的,因此析构函数必须是可以访问。而堆上生成的对象,由于其具体的析构时机由开发者来控制,此时对析构函数的需求不是必须的。
上面是保证了不能在栈上生成对象的原理,接下来我们需要证明我们确实只能在堆上生成它。下面是一个测试的示例(使用了 Qt Creator IDE 及部分 Qt 类):
#include <QCoreApplication>
#include <QDebug>
namespace {
const QString OUTPUT_INFO_SATRT("CreatNewOnly===debug output:");
}
class CreatNewOnly
{
~CreatNewOnly() {
qDebug() << OUTPUT_INFO_SATRT << "~CreatNewOnly() called";
}
public:
CreatNewOnly() {
qDebug() << OUTPUT_INFO_SATRT << "CreatNewOnly() called";
}
void destroy() {
delete this;
qDebug() << OUTPUT_INFO_SATRT << "destroy() called";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// CreatNewOnly test001;
CreatNewOnly* test002 = new CreatNewOnly;
// delete test002;
test002->destroy();
test002 = nullptr;
return a.exec();
}
其输出如下图所示:
这里 CreatNewOnly 与一般对象唯一的区别是它的析构函数是私有的。
我们知道 delete 操作会调用析构函数,但是因为 CreatNewOnly 的析构函数是私有的,所以直接通过 delete 操作指针对象是不能通过编译的。那么我们该如何释放 new 出来的对象呢?我们可以像示例代码一样提供一个公有的成员函数,用于完成 delete 操作。在成员函数中,私有的析构函数是可以访问的。此时的 detele 操作也是可以编译通过的。
下面是注释掉的代码显示的编译问题:
使用场景举例:
- 禁止用户在栈内存空间内创建此类型的对象。要创建对象,只能用 new 在堆上进行。
- 我们设计的类需要在析构时必须要执行一些操作,但是又担心类的使用者不会主动去执行这些操作时。