QDialogs中是否需要析构函数?

塔利亚

我正在跟踪Qt示例(如TabDialog),并且我注意到所有UI项目都是作为指针创建的-但我看不到delete也没有析构函数。

那正确吗 ?那会不会导致内存泄漏?

我正在尝试添加析构函数

~TabDialog()
{
    delete tabWidget;
    delete buttonBox;
}

和来电者

TabDialog *tabDialog = new TabDialog();
tabDialog->setAttribute(Qt::WA_DeleteOnClose);
tabDialog->exec();

但是,当我关闭对话框时,程序崩溃了。

析构函数和delete所有指针项是否都是不必要的,或者我做错了吗?

库巴并没有忘记莫妮卡

抱歉,这只是无法复制。测试用例如下。您可能需要添加一些额外的代码才能重现。

Qt的内存管理将处理所有事务,因为所有小部件最终都带有父级。即:

  1. 选项卡一经传递便成为父项addTab
  2. tabWidgetbuttonBox在将其添加到布局后立即成为父项。

由于您删除了tabWidget和,buttonBox在Qt尝试删除它们之前,一切都很好。删除它们后,系统QObject会立即通知其内存管理,并将其从TabDialog的子级列表中删除我已经在析构函数代码中明确指出了这一点。

的含义Q_ASSERT是:“此时,在运行时,必须满足以下条件”。如果我们错了,调试版本将中止。既然没有,那么断言是正确的。因此,在之前delete tabWidget,对话框同时具有QTabWidgetQDialogButtonBox子级。之后delete tabWidget,对话框中将不再有任何QTabWidget子代。等等。

#include <QApplication>
#include <QDialog>
#include <QTabWidget>
#include <QDialogButtonBox>
#include <QVBoxLayout>

class TabDialog : public QDialog
{
   QTabWidget *tabWidget;
   QDialogButtonBox *buttonBox;
public:
   TabDialog() :
      tabWidget(new QTabWidget),
      buttonBox(new QDialogButtonBox(QDialogButtonBox::Ok | 
                                     QDialogButtonBox::Cancel))
   {
      tabWidget->addTab(new QWidget, tr("General"));
      tabWidget->addTab(new QWidget, tr("Permissions"));
      tabWidget->addTab(new QWidget, tr("Applications"));
      QVBoxLayout *layout = new QVBoxLayout;
      layout->addWidget(tabWidget);
      layout->addWidget(buttonBox);
      setLayout(layout);
      connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
      connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
   }
   ~TabDialog() {
      Q_ASSERT(findChild<QTabWidget*>());
      Q_ASSERT(findChild<QDialogButtonBox*>());
      delete tabWidget;
      Q_ASSERT(! findChild<QTabWidget*>());
      Q_ASSERT(findChild<QDialogButtonBox*>());
      delete buttonBox;
      Q_ASSERT(! findChild<QTabWidget*>());
      Q_ASSERT(! findChild<QDialogButtonBox*>());
   }
};

int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   TabDialog *tabDialog = new TabDialog();
   tabDialog->setAttribute(Qt::WA_DeleteOnClose);
   tabDialog->exec();
   return 0;
}

崩溃的唯一方法是尝试以下操作:

int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   TabDialog *tabDialog = new TabDialog();
   tabDialog->setAttribute(Qt::WA_DeleteOnClose);
   tabDialog->exec();
   // At this point `tabDialog` is a dangling pointer.
   delete tabDialog; // crash
   return 0;
}

不幸的是,Qt示例是无意义的过早悲观的情况。Qt的课程广泛使用PIMPL习惯用法。因此,a的大小QTabWidget并不比a的大小大很多QObject(在我的64位平台上48 vs. 16字节)。通过在堆上分配类的固定成员,您将执行两个堆分配:一小块分配给-QObject派生的类,另一小块分配给其PIMPL。您正无缘无故地将分配数量加倍。

这是避免这种悲观的方法:

#include <QApplication>
#include <QDialog>
#include <QTabWidget>
#include <QDialogButtonBox>
#include <QVBoxLayout>

class TabDialog : public QDialog
{
   QVBoxLayout m_layout;
   QTabWidget m_tabWidget;
   QDialogButtonBox m_buttonBox;
   QWidget m_generalTab, m_permissionsTab, m_applicationsTab;
public:
   TabDialog() :
      m_layout(this),
      m_buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)
   {
      m_tabWidget.addTab(&m_generalTab, tr("General"));
      m_tabWidget.addTab(&m_permissionsTab, tr("Permissions"));
      m_tabWidget.addTab(&m_applicationsTab, tr("Applications"));
      m_layout.addWidget(&m_tabWidget);
      m_layout.addWidget(&m_buttonBox);
      connect(&m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
      connect(&m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
   }
};

int main(int argc, char *argv[])
{
   QApplication app(argc, argv);
   auto tabDialog = new TabDialog();
   tabDialog->setAttribute(Qt::WA_DeleteOnClose);
   tabDialog->show(); // NOT tabDialog->exec()!!
   return app.exec();
}

显式堆分配越少越好。这样,您甚至都不会被delete析构函数吸引住,因为不涉及任何指针。编译器会自动为您生成必要的析构函数调用。

此外,如果您仅在中显示一个窗口main,那么也没有必要进行显式堆分配:

int main(int argc, char *argv[])
{
   QApplication app(argc, argv);
   TabDialog tabDialog;
   tabDialog.show();
   return app.exec();
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章