C++对象析构顺序问题——由QObject::desroyed展开的思考
C++对象析构顺序问题——由QObject::desroyed展开的思考
C++析构函数执行的顺序是最先执行继承链最末端的子类的,最后执行顶层的基类的。
而 QObject::destroyed(QObject* obj = nullptr)
信号在 Qt 文档中说是“在 obj
被完全析构时之前立即触发,并且不会被阻塞”。这里的“完全析构”指的是按照析构函数调用的顺序,存在析构了子类部分但还没析构基类部分的时刻。
因此,下例中会出现先打印析构函数中的输出,然后才调用槽函数的情况。不过,在对象还未被完全析构时,在析构函数中使用其成员(不管是子类部分还是基类部分)都是安全的,因为析构函数的执行是在OS释放这块内存之前。
#include <QCoreApplication>
#include <QDebug>
class MyObjectBase : public QObject
{
Q_OBJECT
public:
MyObjectBase() { qWarning() << "MyObjectBase 构造"; }
virtual ~MyObjectBase() { qWarning() << "MyObjectBase 析构"; }
void ownBaseMethod() { qWarning() << "父类的非虚方法" << m_father; }
virtual void sharedMethod() { qWarning() << "父类的虚方法" << m_father; }
int m_father = 100;
};
class MyObjectDerived final: public MyObjectBase
{
Q_OBJECT
public:
MyObjectDerived() { qWarning() << "MyObjectDerived 构造"; }
virtual ~MyObjectDerived() { qWarning() << "MyObjectDerived 析构"; }
void ownDerivedMethod() { qWarning() << "子类的独有方法" << m_father; }
void sharedMethod() override { qWarning() << "子类重写的虚方法" << m_father << m_child; }
int m_child = 200;
};
class Tracker : public QObject
{
Q_OBJECT
public:
Tracker(MyObjectDerived* target) : target(target) {}
public slots:
void onDestructor() {
qWarning() << "About to be destroyed!";
target->ownBaseMethod();
target->ownDerivedMethod();
target->sharedMethod();
}
private:
MyObjectDerived* target;
};
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
MyObjectDerived *obj = new MyObjectDerived();
Tracker tracker(obj);
QObject::connect(obj, &QObject::destroyed, &tracker, &Tracker::onDestructor);
delete obj;
return app.exec();
}
// moc main.cpp -o main.moc
#include "main.moc"
输出:
MyObjectBase 构造
MyObjectDerived 构造
MyObjectDerived 析构
MyObjectBase 析构
About to be destroyed!
父类的非虚方法 100
子类的独有方法 100
子类重写的虚方法 100 200