【问题标题】:Exception Safety in QtQt 中的异常安全
【发布时间】:2011-03-09 00:39:11
【问题描述】:

Wikipedia 说“一段代码被称为是异常安全的,如果代码中的运行时故障不会产生不良影响,例如内存泄漏、存储数据乱码或无效输出。异常-即使发生异常,安全代码也必须满足放置在代码上的不变量。”

看来我们需要异常处理来保证异常安全。另一方面,在我看来,异常处理在 Qt 应用程序中并不是很流行。

您在 Qt 中满足异常安全的最佳实践是什么?你用什么来代替异常处理?

【问题讨论】:

  • QT 是一个非常古老的库,在异常安全方面非常糟糕。考虑所有小部件如何必须进行堆分配,但 RAII 没有智能指针。您可以做的最好的是将它们分配到 auto_ptr 中,并在插入时将内存释放给父级/布局。他们有一些基本的规定,比如 QMutexLocker,因为在互斥锁被锁定时抛出将是灾难性的,但是他们需要提供更多的东西,或者你必须自己做很多工作才能以异常安全的方式使用 qt .
  • @stinky472:错了。 QSharedPointer
  • @stinky472:哪些 GUI 工具包允许小部件进行堆栈分配?你为什么要这样做?听起来像是一个“稻草人”的论点。
  • @KCArpe 声明是“考虑如何必须为所有小部件进行堆分配***但没有用于 RAII 的智能指针。”我不是在抱怨小部件是堆分配的,通​​过像 QT 这样基于继承和多态的设计,这变得非常重要。我抱怨 QT 的内存管理模型缺乏 RAII 和异常安全性。
  • 看看官方 QT 文档中的这个基本示例:menuBar = new QMenuBar; fileMenu = new QMenu(tr("&File"), this);如果 operator new 在创建 fileMenu 时抛出异常会怎样?糟糕,内存泄漏。同样,QT 并不是为异常安全而设计的。从异常安全的角度来看,即使是诺基亚的例子也不正确。

标签: c++ qt exception exception-handling


【解决方案1】:

我的最佳做法是在基于 Qt 的代码中不使用(或至少尽可能避免使用)C++ 异常,这样处理它们就不是问题了。不过,Qt 并不是真正的原因,我只是觉得异常通常会使事情变得不必要地复杂。但它有助于 Qt 本身主要是无异常问题... :)

【讨论】:

    【解决方案2】:

    Qt(大部分)不是异常安全的:http://doc.qt.io/archives/4.6/exceptionsafety.html

    另一方面,在事件驱动编程中正确处理异常非常困难,所以最好 就是在使用Qt的时候避免它们并传递错误码。

    【讨论】:

    • 为什么在事件驱动编程中正确处理异常非常困难?
    【解决方案3】:

    Qt 类是exception neutral,如documentation 中所述。

    您应该坚持使用布尔值来处理错误情况,就像 Qt 本身一样。

    内部不使用异常是出于可移植性的原因,因为 Qt 必须支持许多不同的平台(可移植性和异常不能很好地混合)。

    再次,来自文档:

    目前,唯一受支持的从 Qt 中抛出的异常(例如由于内存不足)中恢复的用例是退出事件循环并在退出应用程序之前进行一些清理。 典型用例:

    QApplication app(argc, argv);
     ...
     try {
         app.exec();
     } catch (const std::bad_alloc &) {
         // clean up here, e.g. save the session
         // and close all config files.
    
         return 0; // exit the application
     }
    

    【讨论】:

    • 请注意,Qt 的历史可以追溯到新异常出现的时候。 21 世纪的便携性要容易得多。
    • 你说得对,我应该强调这一点。虽然我不太了解异常和 Symbian,尤其是新版本。我知道旧版本使用了截然不同的异常处理机制。
    • Symbian 很痛苦;鉴于其 1980 年代的 EPOC 根源,这并不奇怪。但Qt 和Symbian 没有 相关。 Symbian 起源于 Psion; TrollTech 的 Qt。
    【解决方案4】:

    C++ 有一个非常强大的异常安全机制。为所有因异常而超出范围的变量运行析构函数。这与 Java 等语言不同,Java 等语言的异常安全要求程序员正确使用 catch 和 finally 子句。

    调用析构函数的 C++ 行为与堆栈上的 Qt 对象无缝协作。 Qt 类都有析构函数,没有一个需要手动清理。此外,QSharedPointer<T> 可用于管理堆分配的 Qt 对象;当最后一个指针超出范围时,对象被销毁。这包括指针由于异常而超出范围的情况。

    因此,异常安全当然存在于 Qt 中。它只是透明的。

    【讨论】:

    • 我认为 QSharedPointer 的问题在于它没有在很多地方使用。例如,QLayout 期望 QWidget* 并期望 QLayout 成为它的内存管理器。我们不能在这里使用 QSharedPointer 因为 QLayout 应该在从布局中删除小部件时删除它。 :-(
    • 作者的原始帖子指出,“只要我看到,异常处理在 Qt 应用程序中不是很流行”,这正是事实。 QSharedPointer 的存在没有任何改变,因为 QT API 及其示例中很少有部分实际使用它。相反,我们有像 QButton* button = new QButton(...); 这样的例子。 QVBoxLayout* 布局 = 新 QVBoxLayout(...);在大多数情况下,该库没有被编码为异常安全的。这是应该解决的问题,让库的更多部分接受和存储 QSharedPointer 将是一个非常好的解决方案。
    猜你喜欢
    • 2016-06-16
    • 1970-01-01
    • 2010-11-29
    • 1970-01-01
    • 1970-01-01
    • 2012-02-25
    • 1970-01-01
    • 2010-12-02
    • 2016-11-20
    相关资源
    最近更新 更多