QT特定的堆栈属性与堆属性的区别? [英] QT-specific difference of stack vs. heap attributes?

查看:127
本文介绍了QT特定的堆栈属性与堆属性的区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通常,在编写C ++代码时,我总是将对象保留为常规属性,从而利用RAII.但是,在QT中,删除对象的责任可能在QObject的析构函数之内.因此,假设我们定义了一些特定的小部件,那么就有两种可能性:

Usually, when writing C++ code, I would hold objects always as normal attributes, thus utilizing RAII. In QT, however, the responsibility for deleting objects can lie within the destructor of QObject. So, let's say we define some specific widget, then we have two possibilities:

1)使用QT的系统

class Widget1 : QWidget
{
Q_OBJECT
public:
    Widget1(QWidget* parent = nullptr);

private:
    QPushButton* myButton; // create it with "new QPushButton(this);"
};

2)使用RAII

class Widget2 : public QWidget
{
Q_OBJECT
public:
    Widget2(QWidget* parent = nullptr);

private:
    QPushButton button; // normal RAII
};

通常,我使用第一种方法.如果父母不仅通过布局了解其子女,似乎某事可能会更好地工作.但是考虑一下……真正的原因对我来说还不清楚.

Usually, I use the first method. It seems that something could work better if a parent knows its children not only by layout. But thinking about it... The real reason for this is not quite clear to me.

我知道堆栈是有限的.但是,可以说这在这里不起作用.毕竟,堆栈并不小.

I know that the stack is limited. But let's say that this does not play a role here. After all, the stack is not that small.

推荐答案

如果父母不仅通过布局了解孩子,那么似乎在某些方面工作会更好.

It seems that something could work better if a parent knows its children not only by layout.

你是对的. QObject的父级不仅用于内存管理,此答案总结了它的其他一些用法.这里最重要的是QWidget的那些(因为您担心添加成员QWidget的),因此,如果您在编写时使用第二种方法,那么这里有一些问题可能会得到:

You are right. A QObject's parent is not only used for memory management purposes, this answer sums some of its other usages. The most important ones here are the ones of QWidget's (since you are concerned about adding member QWidget's), so if you use the second approach the way you are writing it, here are some of the issues you might get:

  • 假设您正在实例化Widget1并将其显示在您的主要功能中,如下所示:

  • Suppose you are instantiating Widget1 and displaying it in your main function like this:

Widget1 w;
w.show();

这将显示一个空的窗口小部件,其中没有按钮.与buttonWidget1对象的子代时的行为相反,在其中调用

This will display an empty widget without the button inside it. As opposed to the behavior when button is a child of the Widget1 object, where calling show() displays the widget parent with all of its children inside.

使用 setEnabled() 时,也会发生类似的问题, setLayoutDirection() ,...

Similar issue happens when using setEnabled(), setLayoutDirection(), ...

button.pos() 不会返回相对于Widget1的坐标,实际上该按钮甚至没有显示在其中.使用move()时也会出现同样的问题.

button.pos() won't return coordinates relative to Widget1, in fact the button is not even displayed inside it. Same issue when using move().

事件系统可能无法按预期运行.因此,如果成员窗口小部件不处理某些鼠标/键盘事件,则该事件将不会传播到父级小部件(因为未指定父级).

The event system might not work as expected. So, if the member widget does not handle some mouse/keyboard event, the event won't propagate to the parent widget (since there is no parent specified).

但是可以编写第二种方法来利用与RAII的父关系,从而避免出现上述问题:

But the second approach can be written to utilize the parent relationship with RAII, so that above issues are avoided:

class Widget2 : public QWidget
{
public:
    explicit Widget2(QWidget* parent = nullptr):QWidget(parent){}

private:
    QPushButton button{this}; //C++11 member initializer list
};

或者,在C ++ 11之前的版本中:

Or, in pre-C++11:

class Widget2 : public QWidget
{
public:
    //initialize button in constructor, button's parent is set to this
    explicit Widget2(QWidget* parent = Q_NULLPTR):QWidget(parent), button(this){}

private:
    QPushButton button;
};

这样,两种方法之间的Qt框架没有任何区别.实际上,使用第二种方法可以避免在不必要时进行动态分配(请注意,如果例如在某些嵌套循环中频繁执行分配,这样做可能会带来更好的性能.但是,对于大多数应用程序,性能在这里并不是真正的问题).因此,您可能会像这样编写小部件:

This way there aren't any differences to the Qt Framework between both approaches. In fact, using the second approach avoids dynamic allocation when unnecessary (Note that, this might have slightly better performance, if the allocation is executed very frequently in some nested loop for example. But, for most applications, performance is not really an issue here). So, you might be writing your widgets like this:

class Widget : public QWidget
{
public:
    explicit Widget(QWidget* parent = nullptr):QWidget(parent){
        //add widgets to the layout
        layout.addWidget(&button);
        layout.addWidget(&lineEdit);
        layout.addWidget(&label);
    }
    ~Widget(){}

private:
    //widget's layout as a child (this will set the layout on the widget)
    QVBoxLayout layout{this};
    //ui items, no need to set the parent here
    //since this is done automatically in QLayout::addWidget calls in the constructor
    QPushButton button{"click here"};
    QLineEdit lineEdit;
    QLabel label{"this is a sample widget"};
};

这很好,也许有人会说这些子小部件/对象将被销毁两次,第一次是当它们超出范围时,第二次是销毁其父部件,从而使方法不安全. .这不是不是问题,因为一旦子对象被销毁,它就会从其父对象的子列表中删除自己,请参阅

This is perfectly fine, one might say that those child widgets/objects will be destroyed twice, first when they are out of scope and a second time when their parent is destroyed, making the approach unsafe. This is not an issue as once a child object is destroyed it removes itself from its parent's children list, see docs. So, each object will be destroyed exactly once.

这篇关于QT特定的堆栈属性与堆属性的区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆