【问题标题】:Safe way of creating new QDialog object创建新 QDialog 对象的安全方法
【发布时间】:2021-02-25 01:58:48
【问题描述】:

我正在编写一个 Qt 应用程序,其中 QListWidget 填充有 QListWidgetItems,我想在双击 QListWidgetItem 时创建一个新对话框。所以我在this qt 教程中创建了一个新的对话框类。现在,我添加了on_listWidget_itemDoubleClicked(QListWidgetItem* item) 插槽,我应该在其中创建对话框类的实例。但我希望我的主应用程序仍然可以运行并继续,并且只有对话框窗口显示一些信息。所以我不能只创建一个对话框类的实例并用exec() 函数显示它。

所以我想出的是让我的对话框类中的std::unique_ptr 作为我的主类的类成员,并使用show() 方法调用对话框。每次调用on_listWidget_itemDoubleClicked(QListWidgetItem* item) 时,我都会使用std::make_unique 创建对话框类的新实例,它应该销毁旧对话框并创建一个新对话框。

这通常是一种安全的方法吗?还是有其他解决这种情况的标准方法?

编辑

这是我认为的示例:

主类:

class USB_Packet_Analyzer : public QWidget
{
    Q_OBJECT

public:
    USB_Packet_Analyzer(QWidget *parent = Q_NULLPTR);    
private:
    Ui::USB_Packet_AnalyzerClass ui;
    std::unique_ptr<DataViewer> dataViewer;
private slots:
    void on_listWidget_itemDoubleClicked(QListWidgetItem* item);
};


void USB_Packet_Analyzer::on_listWidget_itemDoubleClicked(QListWidgetItem* item)
{
    dataViewer = std::make_unique<DataViewer>(item, this);
    dataViewer->show();
}

对话框类:

class DataViewer : public QDialog
{
    Q_OBJECT

public:
    DataViewer(QListWidgetItem* item, QWidget *parent = Q_NULLPTR);
    ~DataViewer();

private:
    Ui::DataViewer ui;
};

【问题讨论】:

  • 贴出代码,看来你不需要在某处定义类成员对话框了......

标签: c++ qt


【解决方案1】:

是的,你正在做的很好。您可能需要解决一些小问题:

  1. 关闭对话框不会释放内存。内存将被消耗,直到unique_ptr被销毁。

  2. 关闭对话框时不会清除unique_ptr。现在这可能没问题,但如果您稍后尝试使用 unique_ptr 可能会出现问题。

这些现在可能不会造成问题,但将来可能会。这就是为什么我可以建议以下修改:

void USB_Packet_Analyzer::on_listWidget_itemDoubleClicked(QListWidgetItem* item)
{
    if (!dataViewer.isNull()) dataViewer->deleteLater();
    dataViewer = QPointer(new DataViewer(item, this));
    dataViewer ->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose);
    dataViewer->show();
}

QPointer 可让您轻松检测小部件是否已被删除。

虽然从技术上讲,您使用 unique_ptr 是安全的,但在智能指针中使用 QObject 类是危险的。 QObject 生命周期通常通过父子关系管理。因为智能指针不知道这一点,所以在执行此操作时很容易获得双重释放。因此,我建议您使用QPointer 版本。

【讨论】:

  • 感谢您的回答,我只是对您所说的内容有一些后续问题。如果我错了,请纠正我,但我认为只要我用unique_ptr 指向它,DataViewer 类就会存在,那么怎么会有双重释放呢? unique_ptr 将在每个 on_listWidget_itemDoubleClicked 调用中被重用,它应该停止指向最后一个对话框(我假设这是对话框被销毁并释放内存的地方)并创建新对话框。还是DataViewer 对话框在使用红十字关闭对话框时被破坏,即使unique_ptr 指向它?
  • 在将QObject 类存储在unique_ptr 中的一般情况下,可以在仍位于unique_ptr 中的同时删除对象。 Qt 通过父子关系管理生命周期。当父母被摧毁时,孩子们也被摧毁。 Qt 不知道您对智能指针的使用情况,因此很乐意将其删除。然后,当你的unique_ptr 被销毁时,它又被删除了。默认情况下,对话框在关闭时不会被删除。这就是为什么您的代码在技术上是安全的。但是,通常最好在关闭对话框时将其删除,以将内存释放回系统。
  • 嗯,如果您实际上为您的 DataViewer 对象分配了父级,那么您的代码是不安全的,并且有双重释放的风险。我假设您没有分配父级,因为它是它自己的窗口。如果你给一个对话框一个父级(除了因为你使用unique_ptr而导致的生命周期问题)它不会显示在任务栏中,这是一个糟糕的用户体验。
  • 那么我的选择是: 1. 使用unique_ptr,但创建DataViewer 并以nullptr 作为父级。因此,当USB_Packet_Analyzer 被销毁时,只会被unique_ptr 删除,而不会被Qt 子关系删除。 2. 使用 QPointer 并将 DataViewer 父级设置为 USB_Packet_Analyzer 。因此,当关闭DataViewer 对话框时,由于WA_DeleteOnClose 属性,它将被销毁。但是如果USB_Packet_Analyzer 将关闭而DataViewer 仍然打开怎么办?由于DataViewer是它的孩子,它会因为Qt子关系而被破坏吗?
  • 是的,如果USB_Packet_Analyzer DataViewer 的父级,那么在删除USB_Packet_Analyzer 时它将被删除。请注意(至少在 Windows 上),为 Dialog 提供父级会阻止它在任务栏中显示为单独的 Window。如果您想避免这种情况并且在USB_Packet_Analyzer 时仍然关闭/删除DataViewer,您可以在创建DataViewer 时执行此操作:connect(usbPA, &amp;QObject::destroyed, *dataViewer, &amp;QObject::deleteLater)
【解决方案2】:

你不需要一个特殊的定制对话框来显示几行信息 改用 MessageBox...

 QMessageBox::information(this, "some title", "my text");

【讨论】:

  • 我想在那个对话框中有几个QTableViewsQTreeViews 来显示自定义模型提供的自定义数据,所以它会比仅仅显示一个更复杂一点几行信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-29
  • 2020-05-25
  • 2014-01-02
  • 1970-01-01
  • 2021-10-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多