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

对象树机制

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掉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值