我正在跟踪Qt示例(如TabDialog),并且我注意到所有UI项目都是作为指针创建的-但我看不到delete
也没有析构函数。
那正确吗 ?那会不会导致内存泄漏?
我正在尝试添加析构函数
~TabDialog()
{
delete tabWidget;
delete buttonBox;
}
和来电者
TabDialog *tabDialog = new TabDialog();
tabDialog->setAttribute(Qt::WA_DeleteOnClose);
tabDialog->exec();
但是,当我关闭对话框时,程序崩溃了。
析构函数和delete
所有指针项是否都是不必要的,或者我做错了吗?
抱歉,这只是无法复制。测试用例如下。您可能需要添加一些额外的代码才能重现。
Qt的内存管理将处理所有事务,因为所有小部件最终都带有父级。即:
addTab
。tabWidget
并buttonBox
在将其添加到布局后立即成为父项。由于您删除了tabWidget
和,buttonBox
在Qt尝试删除它们之前,一切都很好。删除它们后,系统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;
}
崩溃的唯一方法是尝试以下操作:
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] 删除。
我来说两句