您的位置:首页 > 编程语言 > Qt开发

Qt从布局管理看对象树机制

2018-01-02 22:21 501 查看

对象树机制

Qt提供了一种机制,在析构父对象时,会先析构它的子对象。完成这种机制需要两个条件:

1、继承自QObject类。

2、指定parent确认父子关系。

布局管理

我们先做一个小实验,我们要把一个QLineEdit显示到Widget上。当然我们可以直接new一个QLineEdit对象,并在它的构造函数传入this指针来实现,还可以使用move函数来移动它的位置。

m_pLineEdit = new QLineEdit(this);
m_pLineEdit->move(100,100);


在这里我们要用水平布局来实现。

不指定parent,也不采用布局是不会在界面上显示QLineEdit的。

m_pLineEdit = new QLineEdit;


不指定parent,采用布局实现

m_pLineEdit = new QLineEdit;

QHBoxLayout *m_pHBoxLayout = new QHBoxLayout;

m_pHBoxLayout->setMargin(10);
m_pHBoxLayout->setSpacing(10);

m_pHBoxLayout->addWidget(m_pLineEdit);

this->setLayout(m_pHBoxLayout);




我们再看一个例子

m_pLineEdit = new QLineEdit;

QHBoxLayout m_pHBoxLayout;

m_pHBoxLayout.setMargin(10);
m_pHBoxLayout.setSpacing(10);

m_pHBoxLayout.addWidget(m_pLineEdit);

this->setLayout(&m_pHBoxLayout);




好,现在我们来看两个问题:

1、第一个例子中的m_pHBoxLayout在堆上创建,我们想要delete掉它时怎么找到这个对象?

2、第二个例子中m_pHBoxLayout在栈上创建,似乎没起到布局作用,但是控件为什么显示到了界面上?

首先,通过以上对比,不难发现,实现我们想要的效果,m_pHBoxLayout的生命周期必须是界面的生命周期,不可以在栈上创建,因为跳出作用域后,它就会被析构掉,所以只能创建在堆上,也难怪各种教程上都是用的指针对象,是由问题本身的特点决定的。

我们明确了m_pHBoxLayout必须得new出来,那么怎么找到它并delete掉呢?聪明得同学想到,把它作为类成员,就可以了。是的,这样可以解决问题。那那么多教程都是以上这么写得,难道他们没有考虑内存泄漏?

这时候我们想到对象树机制,如果是这个机制,那么当界面析构得时候,会把它得子对象m_pHBoxLayout先析构掉,这样就不会造成内存泄漏。

于是,我去查看了setLayout的源码,果然!!

void QWidget::setLayout(QLayout *l)
{
if (!l) {
qWarning("QWidget::setLayout: Cannot set layout to 0");
return;
}
if (layout()) {
if (layout() != l)
qWarning("QWidget::setLayout: Attempting to set QLayout \"%s\" on %s \"%s\", which already has a"
" layout", l->objectName().toLocal8Bit().data(), metaObject()->className(),
objectName().toLocal8Bit().data());
return;
}

QObject *oldParent = l->parent();
if (oldParent && oldParent != this) {
if (oldParent->isWidgetType()) {
// Steal the layout off a widget parent. Takes effect when
// morphing laid-out container widgets in Designer.
QWidget *oldParentWidget = static_cast<QWidget *>(oldParent);
oldParentWidget->takeLayout();
} else {
qWarning("QWidget::setLayout: Attempting to set QLayout \"%s\" on %s \"%s\", when the QLayout already has a parent",
l->objectName().toLocal8Bit().data(), metaObject()->className(),
objectName().toLocal8Bit().data());
return;
}
}

Q_D(QWidget);
l->d_func()->topLevel = true;
d->layout = l;
if (oldParent != this) {
l->setParent(this);
l->d_func()->reparentChildWidgets(this);
l->invalidate();
}

if (isWindow() && d->maybeTopData())
d->topData()->sizeAdjusted = false;
}


题外话:QWidget::setLayout: Attempting to set QLayout “” on Widget “Widget”, which already has a layout这个错误的出处正是这里。

其中

l->setParent(this);
l->d_func()->reparentChildWidgets(this);


说明了这一切。

QLayout继承自QObject,并指定了父对象this,满足对象树机制的条件。

至此第二个问题也很明了了,m_pHBoxLayout虽然是个栈对象,在它销毁之前通过
l->d_func()->reparentChildWidgets(this);
将布局中的控件重新指定了父对象this。于是就出现了以上第二个例子的现象。

总结

采用布局管理的方式进行界面设计,不必手动delete。但是要注意,不满足对象树机制的对象,要谨防内存泄漏,老老实实地把它delete掉。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: