【问题标题】:Basic questions about RAII, STL pop, and PIMPL关于 RAII、STL pop 和 PIMPL 的基本问题
【发布时间】:2011-01-24 04:19:29
【问题描述】:

今天在阅读 proggit 时,我在 post 中看到了这条评论,关于 C++ 如何在 Google Ai 挑战中占据领先地位。用户reventlov声明

我在使用 C++ 时遇到的最大问题是,如果你没有真正理解所有需要理解的东西才能很好地使用 C++,就太容易认为自己是“C++ 程序员”了。

您必须了解 RAII,知道如何使用命名空间,并了解正确的异常处理(例如,您应该能够解释为什么 STL 中的 pop() 方法不返回它们删除的值)。您必须知道标准库中的三代函数中哪一个是正确的。您应该熟悉 PIMPL 等概念。您需要了解标准库(尤其是 STL)的设计是如何工作的。您需要了解宏如何与命名空间交互,为什么通常不应该在 C++ 中使用宏,以及应该使用什么(通常是模板或内联,很少是类)。您需要了解 boost。

我认为我是他提到的那些毫无头绪的 C++ 程序员之一。为了简短起见,我的问题是

  1. 您能否举一个典型的 RAII 监督错误的示例,例如哪些最佳实践规定了 RAII 的使用,但程序员使用其他方式实现了?
  2. 为什么 STL 中的 pop() 方法不返回它们删除的值?
  3. 我阅读了 PIMPL 的 Wikipedia 条目,但没有任何理解。您能否举一个 PIMPL 习语的典型用法示例。

【问题讨论】:

  • 也许你会更高兴留在 reddit 上?这里的规则是一次一个问题。
  • @Neil,哈哈,这将成为你的下一个 10+ 评论 xD
  • 作为一般规则,问题主题应反映所提出的问题。我相信你会因为这个聪明的标题而受到一些关注,但当有人搜索你提到的主题时,它并没有多大帮助。
  • @recipriversexclusion:我认为人们不会善意地为自己的利益而“吸引注意力”。例如,参见meta.stackexchange.com/questions/26305/… 所附的评论。你的帖子应该有自己的优点,而不是因为你选择了一个吸引人但不相关的标题,或者假装赏金(在 joshhunt 的情况下),等等。
  • -1。应该分成单独的问题。对于搜索特定问题的其他人没有用。正如其他人试图指出的那样,这不是一个讨论各种技术的讨论网站,它是一种汇编个人直接问题和评级或批准答案的方式。

标签: c++


【解决方案1】:
  1. RAII 很重要但有时会被遗忘的一个很好的例子是锁定互斥锁时。如果您有一段代码锁定互斥体,执行操作,然后解锁它,如果操作抛出异常或以其他方式导致线程死亡,则互斥体保持锁定状态。这就是为什么有几个作用域锁类(如QMutexLocker),因为正如here 所述,您可以保证析构函数将运行。因此,如果您使用作用域锁,它将始终在销毁时解锁,从而防止死锁。

  2. 为了速度,Pop 返回voidSGI FAQ,并防止对象复制构造函数可能抛出的异常。

  3. PIMPL 被 Qt 框架大量使用以提供二进制兼容性。它允许您对公共 API 隐藏数据结构的所有内部结构。这意味着,如果您想将私有成员添加到类中,请将其添加到 d 指针。这维护了Binary Code Compatibility,因为唯一暴露的数据成员是一个指针。

【讨论】:

  • 第 2 点 +1:SGI 设计 STL 的决定不是根据异常安全性来解释的,而是效率方面的。当然,这并不能说明 C++ 标准化过程是否保留了相同的原理,或者以另一种方式得出了相同的结论。
【解决方案2】:

我会回答第 2 点,其余的留给其他人。 pop 不返回删除值的主要原因是异常安全。

首先,了解 C++ 容器(与 Java 容器不同)按值保存它们的对象。这意味着如果您希望容器在弹出时返回对象,它必须通过复制被删除的对象按值返回该对象。相反,通过让您在pop 之前访问top,它可以简单地返回对顶部元素的引用,并且您可以在弹出它之前将其复制到您心中的内容。 (然而,如果 pop 通过引用返回元素,它将是一个悬空引用,因为该对象不再在容器中。)

使pop 按值返回的后果(除了复制被删除对象所涉及的低效率)是它危及异常安全。理想情况下,如果一个操作抛出一个异常,所涉及的对象的状态是不变的。但是如果pop 按值返回被移除的对象,如果该对象的复制构造函数失败了怎么办?对象已经从容器中移除,因此状态已经改变。

这比我想写的要冗长,但希望能让您了解为什么 pop 返回值是个坏主意。

【讨论】:

  • 这并不能解释为什么标准库中的其他 mutator 函数确实按值返回非内置类型(通常是迭代器,其构造函数不太可能能够抛出)。但是,是的,他们不提供强大的异常保证这一事实并不意味着 top/pop 不应该。
  • template<class C> typename C::value_type pop(C& c) { typename C::value_type copy = c.top(); c.pop(); return copy; } 唯一的例外安全问题是项目的 dtor 是否可以抛出,但抛出的 dtor 应该非常很少见,并且不抛出的 dtor 很容易成为调用此 pop() 的要求,类似于其他容器要求。
  • @Roger:这很好,但是(在异常安全方面)编译器不需要实现 RVO,在这种情况下,return copy 将导致(再次)调用复制构造函数。 (PS Throwing dtors 是非常坏的消息(tm),因为如果它发生在堆栈展开期间,std::terminate 会被调用,或者类似的东西。所以人们可以/应该安全地假设这不应该发生。)
  • 是的,返回时可能会抛出,但这并不违反异常安全:我的 pop 函数仍然做出基本保证(boost.org/community/exception_safety.htmlen.wikipedia.org/wiki/Exception_handling#Exception_safety),因为容器始终处于一致状态state 和任何 dtors 都会被执行。
【解决方案3】:

阅读 Herb Sutter 的“Exceptional C++”和“Exceptional C++ Style”。

【讨论】:

  • 我正要这么说。 “卓越的 C++”非常彻底地涵盖了这三个主题(以及其他主题)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-27
  • 2013-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多