qt中使用对象树的方式来管理对象。所谓对象树就是对象与对象之间存在树状关系。一个父对象可以含有多个子对象,但是一个子对象只能含有一个父对象。当父对象析构时,会自动析构其所含有当所有子对象。如:
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr, QString name="")
:QWidget(parent),m_name(name){}
~Widget(){ qDebug() << m_name << "析构了"; };
QString m_name;
};
p_a:{
Widget w1(nullptr, "w1");
Widget *w2 = new Widget(&w1, "w2");
}
当p_a程序段运行结束时,会按照变量声明的顺序反向析构变量。所以w2应当会先被析构,然后析构w1。但是w2是new出来当指针,需要调用delete才能析构。所以会跳过w2直接析构w1。析构w1时,发现其含有子对象w2,于是在w1析构完成后会继续析构w2,所以最终的运行结果如下:
"w1" 析构了
"w2" 析构了
使用对象树来管理对象时需要注意以下几点:
- 如果子对象先析构了,然后再析构父对象,这时并不会对程序造成影响;
- 如果父对象由析构了,然后再析构子对象,这时程序会发生异常。
原因分析:这是因为通过析构父对象来析构子对象时会先判断子对象是否被析构,如果已经被析构,则不再析构它。而如果是在子对象生命周期结束或者调用delete来析构时,则不会做这个判断。如:
p_b:{
Widget w1(nullptr, "w1");
Widget w2(nullptr, "w2");
p_a:{
Widget *w3 = new Widget(&w1, "w3");
w3->setParent(&w2);
w2.setParent(&w1);
delete w3;
}
qDebug() << "程序段p_a结束了";
}
qDebug() << "程序段p_b结束了";
运行结果:
"w3" 析构了
程序段p_a结束了
"w2" 析构了
"w1" 析构了
程序段p_b结束了
如果去掉p_a中的delete w3,则运行结果为:
程序段p_a结束了
"w2" 析构了
"w3" 析构了
"w1" 析构了
程序段p_b结束了
如果将w2和w1的声明顺序交换,即
p_b:{
Widget w2(nullptr, "w2");
Widget w1(nullptr, "w1");
p_a:{
Widget *w3 = new Widget(&w1, "w3");
w3->setParent(&w2);
w2.setParent(&w1);
//delete w3;
}
qDebug() << "程序段p_a结束了";
}
qDebug() << "程序段p_b结束了";
则程序发生异常:
程序段p_a结束了
"w1" 析构了
"w2" 析构了
"w3" 析构了
程序异常结束。
针对以上描述,在使用对象树时,有以下建议:
- 父对象总是在子对象之前声明;
- 尽量使用指针来声明子对象。这样就可以减少子对象因生命周期结束而提前析构导致的程序异常。
- 不要对表示子对象的指针调用delete操作。
本文介绍了Qt中的对象树管理,说明了对象树中父对象与子对象的关系,以及析构时的顺序。当父对象析构时会自动析构其所有子对象,但如果子对象先析构或手动调用delete,可能会导致程序异常。为避免问题,建议父对象先声明,使用指针声明子对象,并避免对子对象指针调用delete。

601

被折叠的 条评论
为什么被折叠?



