1 Qt对象间的父子关系
1.1 Qt对象间的父子关系概述
Qt对象间可以存在父子关系:
- 每一个对象都保存有它所有子对象的指针。
- 每一个对象都有一个指向其父对象的指针。
当指定Qt对象的父对象时: - 其父对象会在子对象链表中加入该对象的指针。
- 该对象会保存指向其父对象的指针。
编程实验:对象间的父子关系
void fcTest()
{
QObject* p = new QObject();
QObject* c1 = new QObject();
QObject* c2 = new QObject();
c1->setParent(p);
c2->setParent(p);
qDebug() << "c1: " << c1;
qDebug() << "c2: " << c2;
const QObjectList& list = p->children();
for(int i=0; i<list.length(); i++)
{
qDebug() << list[i];
}
qDebug() << "p: " << p;
qDebug() << "c1 parent: " << c1->parent();
qDebug() << "c2 parent: " << c2->parent();
}
当Qt对象被销毁时:
- 将自己从父对象的Children List移除。
- 将自己的Children中的所有对象销毁。
使用Qt开发时,不仅要时刻注意内存泄漏的问题,还要实际可关注对象是否可能被多次销毁的问题!
Qt中对象树的概念:
- 利用Qt对象间的父子关系可以构成对象树。
- 删除树中的节点时会导致对应的子树被销毁。
编程实验:对象的删除
#include <QtCore/QCoreApplication>
#include <QDebug>
class MObj : public QObject
{
QString m_name;
public:
MObj(const QString& name)
{
m_name = name;
qDebug() << "Constructor: " << m_name;
}
~MObj()
{
qDebug() << "Destructor: " << m_name;
}
};
void delTest()
{
MObj* obj1 = new MObj("obj1");
MObj* obj2 = new MObj("obj2");
MObj* obj3 = new MObj("obj3");
MObj* obj4 = new MObj("obj4");
obj2->setParent(obj1);
obj3->setParent(obj1);
obj4->setParent(obj3);
delete obj3;
const QObjectList& list = obj1->children();
qDebug() << "obj2: " << obj2;
for(int i=0; i<list.length(); i++)
{
qDebug() << list[i];
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
delTest();
return a.exec();
}
1.2 关于Qt对象间的父子关系的疑问
代码如下:
#include <QtCore/QCoreApplication>
#include <QDebug>
#include <QString>
class Test : public QObject
{
private:
QString m_name;
public:
Test(QString name, QObject* parent)
{
m_name = name;
qDebug() << "construct : " << m_name;
setParent(parent);
}
~Test()
{
qDebug() << "deconstruct : " << m_name;
}
};
class App : public QObject
{
private:
Test t1;
Test t2;
Test* pt3;
public:
App() : t1("t1", this), t2("t2", this)
{
qDebug() << "construct : App";
pt3 = new Test("pt3", this);
}
~App()
{
qDebug() << "deconstruct : App";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
App* app = new App;
/*
* 这里*app和其成员变量t1、t2之间是父子关系,按理说我delete app指针的时候,会将其所有的子对象进行销毁(这应该调用t1和t2的析构函数),
* 然后按照析构函数的调用顺序,会调用t1和t2的析构顺序。这样的话t1和t2的析构函数就会调用两次。而实际上只调用了一次,这是为什么呢?
* 输出结果如下:
* construct : "t1"
construct : "t2"
construct : App
deconstruct : App
deconstruct : "t2"
deconstruct : "t1"
deconstruct : "pt3"
从上面的输出结果可以看出t2、t1是自己释放的,而不是由父对象释放的,这是为什么呢?
哎,自己被自己误导,上面的输出结果是正确的:会先调用App本身的析构函数,然后再调用成员变量的析构函数(调用成员变量的析构函数时就会将自己从父对象的children列表中删除),最后调用父对象的析构函数,在最底层的QObject析构函数中才会去对children列表中的对象进行删除。
*/
delete app;
return a.exec();
}
输出结果为:
参考资料: