有一个小部件声明顺序要遵循吗?

There's a widget declaration order to follow?

是否有在 Qt5 中声明小部件的顺序(也许还有 4 个)?

考虑以下代码片段:

(只是header的一部分帮我解释一下)

class ConfigDialog : public QDialog
{
    Q_OBJECT

    QGroupBox userAuthBox;
    QGridLayout userAuthLayout;
    QVBoxLayout dialogLayout;

    QLabel userLabel;
    QLabel passLabel;
    QLineEdit userEdit;
    QLineEdit passEdit;
};

这按预期工作但只是更改为(重新排序声明):

class ConfigDialog : public QDialog
{
    Q_OBJECT

    QLabel userLabel;
    QLabel passLabel;
    QLineEdit userEdit;
    QLineEdit passEdit;
    QGroupBox userAuthBox;
    QGridLayout userAuthLayout;
    QVBoxLayout dialogLayout;
};

这也有效,但是当 ConfigDialog 超出范围时会发生段错误。

我也在其他场景中看到过这种情况,但总是更改顺序可以解决此问题。

我的猜测是:您使 QGroupBox 成为其他一些小部件的 parent。

Qt 在 QObject 之间有一个 parent-child 关系的概念。 parent负责在自身被销毁时删除其children;假设那些 children 被分配在堆上 new.

此外,C++ class 的数据成员按照它们在 class 中列出的顺序构造,并以相反的顺序销毁。

假设 userAuthBoxuserLabel 的 parent(通过 setParent 调用,在您的情况下由 addWidget 执行)。在第一种情况下,userLabel 首先被销毁,并通知其 parent 这一事实,随后 userAuthBox 将其从其 child 小部件列表中删除,并且不再尝试删除它。

在第二种情况下,userAuthBox 首先被销毁,并在其指向 userLabel 的指针上使用 delete。但是当然 userLabel 实际上并没有分配给 new。该程序然后表现出未定义的行为。

TL;DR:是的!声明的顺序在 C++ 中有严格定义的含义。正如您碰巧注意到的那样,随机顺序将不起作用。

您没有显示所有代码。重要的是其中一个小部件是组框的子项。假设你有:

class ConfigDialog : public QDialog
{
  // WRONG
  Q_OBJECT
  QLabel userLabel;
  QGroupBox userAuthBox;
  QGridLayout userAuthLayout;
  QVBoxLayout dialogLayout;
public:
  ConfigDialog(QWidget * parent = 0) :
    QDialog(parent),
    dialogLayout(this),
    userAuthLayout(&userAuthBox) {
    // Here userLabel is parent-less.
    Q_ASSERT(! userLabel.parent());
    userAuthLayout.addWidget(&userLabel, 0, 0);
    // But here userLabel is a child of userAuthBox
    Q_ASSERT(userLabel.parent() == &userAuthBox);
  }
};

默认析构函数将按以下顺序调用析构函数 - 就好像您在析构函数中编写了以下有效的 C++ 代码一样。

  1. dialogLayout.~QVBoxLayout() - 好的。此时,对话框只是无布局。所有子部件都保留。
  2. userAuthLayout.~QGridLayout() - 好的。此时,组框只是无布局。所有子部件都保留。
  3. userAuthBox.~QGroupBox() - 糟糕。由于 userLabel 是此对象的子对象,因此嵌套的 userAuthox.~QObject 调用将执行以下行的等效项:

    delete &userLabel;
    

    由于从未使用 new 分配 userLabel,您会得到未定义的行为,并且在您的情况下会发生崩溃。

相反,您应该:

  1. 在其父项之后声明子控件和 QObject

  2. 如果可能,使用 C++11 值初始化,或在构造函数中列出初始化程序,以向维护者表明子项与父项之间存在依赖关系。

有关详细信息和 C++11 和 C++98 解决方案,请参阅 ,该解决方案将迫使所有流行的现代静态 C++ 代码分析器捕获错误。如果可以,请使用它们。