关于RAII,STL pop和PIMPL的基本问题

rec*_*ion 9 c++

在今天阅读proggit时,我在一篇文章中发表了这篇评论,关于Google Ai挑战中的顶级位置是如何被C++采用的.用户reventlov声明

我在C++中遇到的最大问题是,如果没有真正理解使用C++可以理解的所有需要​​,那么你很容易认为自己是"C++程序员".

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

我想我是他提到的那些无能的C++程序员之一.为了简短起见,我的问题是

  1. 您能举例说明典型的RAII监督错误,例如最佳实践规定使用RAII但程序员是否已采用其他方式实施?
  2. 为什么 STL中的pop()方法没有返回它们删除的值?
  3. 我阅读了PIMPL的维基百科条目,对此一无所知.您能举例说明PIMPL习惯用法的典型用法吗?

Chr*_*ung 9

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

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

做的后果pop由价值回归(除了参与复制被删除对象的低效率)是它危及异常安全.理想情况下,如果操作抛出异常,则所涉及对象的状态不变.但是如果pop要按值返回被删除的对象,那么该对象的复制构造函数如何失败呢?该对象已经从容器中删除,因此状态已经改变.

这比我想做的更有语言,但希望能让你知道为什么pop返回一个值是一个坏主意.

  • 这并不能解释为什么标准库中的其他mutator函数会按值返回非内置类型(通常是迭代器,其构造函数*不太可能*能够抛出).但是,是的,他们没有提供强有力的例外保证并不意味着顶级/流行不应该. (2认同)

Ada*_*m W 9

  1. 一个很好的例子,其中RAII是至关重要但有时被遗忘的是锁定互斥锁时.如果您有一段锁定互斥锁的代码,执行操作,然后将其解锁,如果操作抛出异常或导致线程死亡,则互斥锁保持锁定状态.这就是为什么有几种范围的锁类(比如QMutexLocker因为作为陈述)在这里,你保证析构函数会运行.因此,如果您使用范围锁定,它将始终在销毁时解锁以防止死锁.

  2. void为了速度,Pop返回:SGI FAQ,以及防止对象复制构造函数可能抛出的异常.

  3. Qt框架大量使用PIMPL来提供二进制兼容性.它允许您从公共API隐藏数据结构的所有内部.这意味着,如果要将私有成员添加到类中,则将其添加到d指针.这保持了二进制代码兼容性,因为暴露的唯一数据成员是指针.

  • 点2的+1:SGI在设计STL时的决定没有用例外安全性来解释,而是效率.当然,这并没有表明C++标准化过程是保留相同的基本原理,还是以另一种方式得出相同的结论. (2认同)