C ++ - 为什么我要在堆上创建这些小部件?

时间:2013-09-24 14:50:28

标签: c++ qt

使用C ++和Qt创建GUI时,您可以创建一个标签,例如:

QLabel* label = new QLabel("Hey you!", centralWidgetParent);

这会在堆上创建对象并保持在那里,直到我手动删除它或者父进程被销毁。我现在的问题是为什么我需要一个指针呢?为什么不在堆栈上创建它?

//Create a member variable of Class MainWindow
QLabel label;

//Set parent to show it and give a text so the user can see it
    QWidget* centralWidget = new QWidget(this); //Needed to add widgets to the window
    this->setCentralWidget( centralWidget ); 
    label.setParent(centralWidget);
    label.setText( "Haha" );

这很好用,我可以看到标签并没有消失。

我们在C ++中使用指针让事情更长寿,所以我们可以在各种范围内使用对象。但是当我创建一个成员变量时,它会不会停留直到对象被破坏?

编辑: 也许我没有说清楚。这是MainWindow类:

class MainWindow : public QMainWindow
{
    Q_OBJECT
    QLabel label; //First introduced here...

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
};

//Constructor
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QWidget* centralWidget = new QWidget(this);
    this->setCentralWidget( centralWidget );
    label.setParent(centralWidget);
    label.setText( "Haha" );
}

5 个答案:

答案 0 :(得分:8)

如果label超出范围,将调用析构函数(QLabel::~QLabel)。来自docs

  

销毁该对象,删除其所有子对象。

没有必要在堆上创建对象 - 您可以将对象放在堆栈上,但是您需要对对象的生命周期负责(关于在堆上分配数据的问题最大的问题之一是“谁和什么时候应该删除这些对象?”的问题,在Qt中它由层次结构处理 - 每当你删除你的小部件时,所有的子小部件都将被删除。)

为什么你的程序有效 - 我不知道 - 它可能不起作用(label在范围的末尾被销毁)。另一个问题是 - 如果您没有引用它,您将如何更改label的文本(例如,从某个插槽中)?

修改我刚看到您的labelMainWindow的成员。拥有一个完整的对象,而不是指向作为类成员的对象的指针是完全没问题的,因为在MainWindow之前它不会被销毁。请注意,如果您创建MainWindow的实例,请执行以下操作:

MainWindow *w = new MainWindow();
将在堆上创建

label

答案 1 :(得分:4)

因为这就是Qt的设计方式。我意识到这不是一个非常令人满意的答案,但是Qt被简单地设计为“在堆上创建小部件,父母负责删除他们的孩子”。

意识到Qt的起源比我们认为的“现代C ++”更古老,更早。

答案 2 :(得分:4)

要记住的另一件事是Qt使用所谓的pimpl范例,其中对象数据在您实际实例化的类后面隐式共享和管理。见这里:http://qt-project.org/wiki/Dpointer

最重要的是,如果你要在堆栈上进行分配以避免使用堆,那么Qt只是在你身上拉一个快速的并且无论如何都要使用堆。

答案 3 :(得分:3)

将窗口小部件分配为局部变量通常不是一个好主意,因为通常它会在以任何方式有用之前超出范围;因为QObject通过其“父子关系”(与C ++析构函数很好地集成)支持组合模式,所以通常最简单的事情就是利用这样的特性。

另一方面,可以使其成为MainWindow类的成员,或者通常以任何方式分配它,使其生命周期更少比其父母的生命周期。事实上,当这样的QLabel被销毁时,它会自动从其父级注销,避免双重释放。但通常更方便的是将小部件分配到堆上,将它们注册为当前窗口的子级,因为通常在创建它们之后实际上不需要访问许多小部件(例如标签),因此不必杂乱你的班级有无用的数据成员。你只需要new QLabel(this, ...)进入你的窗口构造函数就可以了。

所做的是,如果他们的生命周期长于其父级的生命周期(例如将它们放在全局变量或静态变量中),则分配不带new的小部件) - 执行此操作将导致父级尝试delete它们的销毁,这将导致最坏的崩溃,最坏的情况下无声的内存损坏。这个可以修复(通过手动取消注册类析构函数中的小部件),但我无法想象任何这样的事情会有用的场景。

答案 4 :(得分:1)

主要原因 - 动态创建/删除小部件的可能性。 QObject(和QWidget)设计为无法复制构造函数的类,因此无法传递参数(通过值/引用)。因此,在所有情况下使用指针使代码更简单明了。