C++ QDialogs中是否需要析构函数?
我遵循Qt示例(如),我注意到所有UI项都是作为指针创建的,但我没有看到C++ QDialogs中是否需要析构函数?,c++,qt,memory-leaks,C++,Qt,Memory Leaks,我遵循Qt示例(如),我注意到所有UI项都是作为指针创建的,但我没有看到delete和析构函数 是这样吗?这不会导致内存泄漏吗 我正在尝试添加析构函数 ~TabDialog() { delete tabWidget; delete buttonBox; } 打电话的人 TabDialog *tabDialog = new TabDialog(); tabDialog->setAttribute(Qt::WA_DeleteOnClose); tabDialog->ex
delete
和析构函数
是这样吗?这不会导致内存泄漏吗
我正在尝试添加析构函数
~TabDialog()
{
delete tabWidget;
delete buttonBox;
}
打电话的人
TabDialog *tabDialog = new TabDialog();
tabDialog->setAttribute(Qt::WA_DeleteOnClose);
tabDialog->exec();
但是当我关闭对话框时程序崩溃了
析构函数和删除所有指针项是不必要的还是我做错了?我想你会因为这些行而感到困惑:
tabWidget = new QTabWidget;//and so on
您看不到显式父对象(如newqtabwidget(this);
),但这里不需要显式父对象。请看这里:
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(tabWidget);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);
setLayout
将重新出租您的QVBoxLayout
和QVBoxLayout
将重新出租其中的所有小部件,因此现在您的小部件有一个父部件,它们将在您的对话框后被销毁
如前所述:
使用布局时,不需要传递父级
构建子窗口小部件。布局将自动重新出租
小部件(使用QWidget::setParent())以便它们是
安装布局的小部件
注意:布局中的小部件是
安装的是布局,而不是布局本身。小部件只能具有
其他小部件作为父部件,而不是布局
Qt上的内存处理
Qt将小部件作为一个树来处理,每个小部件都有一个父部件,每个父部件都有一个
释放子部件内存的义务,如果小部件没有父部件,您应该使用操作员
delete
手动删除它。很抱歉,这不会重现。下面是测试用例。您可能需要添加一些额外的代码以使其重现
Qt的内存管理将处理所有事情,因为所有的小部件最终都有父部件。即:
addTab
时即被设置为父级tabWidget
和buttonBox
一旦添加到布局中,它们就会成为父项tabWidget
和buttonBox
,因此一切正常。一旦删除它们,QObject
的内存管理将被通知,它们将从TabDialog
的子列表中删除。我在析构函数代码中明确了这一点
Q_ASSERT
的含义是:“在运行时的这一点上,以下内容必须为真”。如果我们错了,调试生成将中止。因为它不是,所以断言是正确的。因此,在delete tabWidget
之前,该对话框同时具有QTabWidget
和QDialogButtonBox
子项。在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;
}
不幸的是,Qt的例子是毫无意义的过早悲观。Qt的类广泛地使用了这个习惯用法。因此,比如说QTabWidget
的大小并不比QObject
的大多少(在我的64位平台上是48字节对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();
}
如果您构造dialog的小部件并将对话框设置为父级,则不需要在对话框的析构函数中删除它们:Qt将负责删除它们。抱歉,这不会重现。你的代码是正确的。删除是不必要的,但无害的。@KubaOber谢谢,我不确定,因为我希望有一种新+删除模式,而且我不想造成内存泄漏。您确定紧跟在
tabDialog->exec()
之后的代码没有试图访问tabDialog
?请记住,当exec()
返回时,tabDialog
是一个悬空指针。也许你有delete tabDialog
就在它下面,而你却对我们隐藏了它?!选项卡dialog->exec()之后没有代码。。。所述函数上没有其他代码,称为menu.addAction(showPropertiesAct)代码>在上下文菜单event.istabDialog->setAttribute(Qt::WA_DeleteOnClose)中代码>添加的好主意?还是默认设置?@Thalia当用户关闭对话框时,它将删除该对话框。如果您需要它,并且使用tabDialog->show()调用dialog,这是非常好的因为在您的例子中,exec()
“停止”执行并等待结果,所以您可以编写类似于tabDialog->exec()的代码;删除选项卡对话框代码>你的答案有误导性。销毁受Qt内存控制的小部件不是无效的。这里的问题是delete
被传递给悬空指针。Thalia并没有删除现有的小部件——这将是一件非常有效的事情。“Qt”不会删除您已经删除的小部件,这将非常糟糕。Qt实际上跟踪父对象的QObject
s的生命周期,如果您先删除对象,那么Qt的行为非常恰当。同样,这里的错误是,delete
传递的悬空指针不指向有效的QObject
实例,而是指向垃圾!如果您是对的,此代码将无法工作:intmain(intargc,char**argv){QApplication应用程序(argc,argv);QWidget w;QVBoxLayout l(&w);QLabel标签(“Hello”);QPushButton按钮(“button”);l.addWidget(&label);l.addWidget(&button);w.show();return app.exec()}
然而,尽管在退出main
时按顺序执行以下析构函数调用,它仍然可以工作:1<代码>按钮。~QPushButton()
,2<代码>标签。~QLabel()
,3<代码>l.~QVBoxLayout()
,4<代码>w.~QWidget()
,5<代码>应用程序~QApplication()
。这些调用是C++中的有效C++,BTW. @切尔诺贝利,对象从大多数派生到基类被破坏。代码>选项卡
#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();
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
TabDialog tabDialog;
tabDialog.show();
return app.exec();
}