欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 八卦 > qt对象树析构详细顺序

qt对象树析构详细顺序

2025/2/25 17:35:53 来源:https://blog.csdn.net/kangkanglhb88008/article/details/141888020  浏览:    关键词:qt对象树析构详细顺序

既然构成对象树了,那么说明一定最底层是继承自QObject类的

先说答案:

非QWidget类对象 和 QWidget类对象 析构过程还不太一样,这里分别进行讲解:(来源于qt底层源码分析)

一、非QWidget类对象

  1. 本对象A的析构函数,然后本对象中的栈成员析构(按照定义的顺序依次进行)
  2. 父对象析构函数,同理该对象中的栈成员析构...
  3. 父父对象析构函数......
  4. QObject析构函数,包含两个操作:
    1. 如果不是QWidget类对象,则emit destroyed(this);
    2. 断开所有自己连接的槽以及自己被连接的信号
    3. delete所有子对象if (!d->children.isEmpty()) d->deleteChildren();。delete子对象操作,又会触发子对象的析构操作,就这样递归进行下去......
  5. 终于先delete完所有子对象,QObject析构函数结束,到此为止,本对象A完全销毁完成。

二、QWidget类对象

  1. 本对象A的析构函数,然后本对象中的栈成员析构(按照定义的顺序依次进行)
  2. 父对象析构函数,同理该对象中的栈成员析构...
  3. 父父对象析构函数......
  4. QWidget析构函数,包含操作:
    1. 清理持有的焦点,快捷键以及界面等信息
    2. 如果有连接别的对象的槽函数if (d->isSignalConnected(0)),则发射emit destroyed(this);
    3. 删除所有孩子对象,    if (!d->children.isEmpty()) d->deleteChildren();
    4. 移除所有关于自己的事件QCoreApplication::removePostedEvents(this);
    5. 发送QEvent::Destroy事件QCoreApplication::sendEvent(this, &e);
  5. QObject析构函数,包含两个操作:
    1. 断开所有自己连接的槽以及自己被连接的信号
    2. delete所有子对象if (!d->children.isEmpty()) d->deleteChildren();。delete子对象操作,又会触发子对象的析构操作,就这样递归进行下去......
  6. 终于先delete完所有子对象,QObject析构函数结束,到此为止,本对象A完全销毁完成。

所以,千万别在孩子对象析构函数中(qt对象树特有的这个连带析构关系)去访问父对象的任何栈成员(包括堆成员)的信息了,因为这些成员的内存已经释放了,所以可能导致隐形的崩溃,问题是比较难查的了。

如果非要在子子对象析构函数里访问父对象信息,当父对象仍然存在时是没有问题的(例如Tabwidget包含多个tab页面,手动一个一个先叉掉这些页面,也就是先析构它们,这样就能保证父对象一直存在,即安全的),但是如果是直接叉掉Tabwidget,希望自动析构多个子tab页面,此时就不能在子对象析构函数里访问父对象信息的操作了,如果非要这样干,那就在父对象Tabwidget的析构函数里先一个个子对象的delete吧,这样也是安全了,就是多写几行代码了。

讨论:

还有个方法也安全:子对象里用发射信号的方式,让父对象执行目标操作,这样为什么安全呢,因为父对象如果已经被析构了,大不了这个槽函数不执行,也不会崩溃的。

这个想法对于父对象是非QWidget类是成立的,因为从上面分析一可以看出确实是先断开了所有信号槽的,但是对于父对象是QWidget类是不对的,因为从上面分析二可以看出QWidget析构函数里就先删除孩子对象(此时信号槽还没有断开),此时孩子对象析构函数里发射信号,父对象仍然是可以执行的。这是这个问题的验证源码工程:https://download.csdn.net/download/kangkanglhb88008/89717506?spm=1001.2014.3001.5503

解决方法:但是子对象(这里也称本对象)中可以这样干,关联对方的destroyed信号,然后本对象实时执行断开之前和它绑定的所有信号即可,这样本对象析构函数里发射的信号,对方的槽就不会执行了即:

    connect(this, &A::sigPrint, pMainWindow->getFormC(), &FormC::onPrint);
    connect(pMainWindow->getFormC(), &FormC::destroyed, [this](){
        disconnect(this, &A::sigPrint, pMainWindow->getFormC(), &FormC::onPrint);
    });

总结:

所以不要在析构函数里,访问父对象或者父父对象...的任何成员,因为本对象之所以进入析构函数,本身就可能是析构树连带下来析构了自己的,所以此时父对象或者父父对象...可能已经主体部分不存在了的,不然造成隐性问题崩溃。要访问,就访问能确保一定存在的对象,例如全局变量等

第一个实验源码:

类A

#ifndef A_H
#define A_H#include <QObject>class B;class A : public QObject
{Q_OBJECT
public:explicit A(QObject *parent = nullptr);~A();B* parentB = nullptr;signals:
};#endif // A_H#include "a.h"
#include "b.h"#include <QDebug>A::A(QObject *parent): QObject{parent}
{parentB = (B*)parent;
}A::~A()
{parentB->getA2()->fun("now do getA2()->fun");qDebug()<<"~A";
}

类A2

#ifndef A2_H
#define A2_H#include <QObject>class A2 : public QObject
{Q_OBJECT
public:explicit A2(QObject *parent = nullptr);~A2();void fun(QString str);signals:
};#endif // A2_H#include "a2.h"#include <QDebug>A2::A2(QObject *parent): QObject{parent}
{}A2::~A2()
{qDebug()<<"~A2";
}void A2::fun(QString str)
{qDebug()<<"A2::fun called "<<str;
}

类B

#ifndef B_H
#define B_H#include <QObject>#include "a.h"
#include "a2.h"class B : public QObject
{Q_OBJECT
public:explicit B(QObject *parent = nullptr);~B();A2* getA2(){return &a2;}signals:private:A2 a2;A* a = nullptr;A* aa = nullptr;
};#endif // B_H#include "b.h"#include <QDebug>B::B(QObject *parent): QObject{parent}
{a = new A(this);aa = new A(this);
}B::~B()
{qDebug()<<"~B";
}

类Mainwindow

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>#include "a.h"class B;QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void on_pushButton_clicked();private:Ui::MainWindow *ui;B* b;
};
#endif // MAINWINDOW_H#include "mainwindow.h"
#include "ui_mainwindow.h"#include "b.h"#include <QDebug>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);b = new B();
}MainWindow::~MainWindow()
{qDebug()<<"~MainWindow";delete ui;
}void MainWindow::on_pushButton_clicked()
{qDebug()<<"do delete b";delete b;
}

其它可供参考博客:

Qt学习记录(4)——对象树_qt的对象树-CSDN博客

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词