2010-03-05 77 views
9

今天在阅读proggit时,我在post上发表了关于Google Ai挑战中的顶级位置是如何被C++采用的评论。用户reventlov声明关于RAII,STL流行音乐和PIMPL的基本问题

我与C++最大的问题是,它是waaay太容易认为你是一个“C++程序员”并没有真正理解你需要了解使用C++可以接受很好的事情。你必须知道RAII,并知道使用名称空间,并理解适当的异常处理(例如,你应该能够解释为什么STL中的pop()方法不返回它们移除的值) 。你必须知道标准库中三代函数中哪一个是正确的。你应该熟悉像PIMPL这样的概念。您需要了解标准库(尤其是STL)的设计是如何工作的。您需要了解宏与命名空间的交互方式,以及为什么您通常不应该在C++中使用宏以及您应该使用什么(通常是模板或内联,很少是类)。你需要知道关于提升。

我想我是他提到的那些无知的C++程序员之一。为了保持这个简短,我的问题是

  1. 你可以举一个典型的RAII监督错误的例子,例如,哪里有最佳实践要求使用RAII,但程序员是否采用其他方式实施?
  2. 为什么 STL中的pop()方法返回它们删除的值?
  3. 我读了PIMPL的维基百科条目,不理​​解其中的任何内容。你能举一个典型的PIMPL习惯用法的例子吗?
+0

我不明白问题1,你能改说吗? – 2010-03-05 15:41:49

+21

也许你会更高兴留在reddit?这里的规则是一次一个问题。 – 2010-03-05 15:42:48

+2

@尼尔,大声笑这将成为你的下一个10+评论xD – 2010-03-05 15:45:09

回答

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

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

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

+2

第2点的+1:SGI在设计STL时的决定没有在异常安全性方面解释,而是在效率方面。当然,这并不表明C++标准化过程是保留相同的原理还是以另一种方式得出相同的结论。 – 2010-03-05 20:03:16

3

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

+0

我只是想说。 “Exceptional C++”非常全面地涵盖了这三个主题(和其他主题)。 – 2010-03-05 16:15:23

9

我会回答第2点,剩下的给其他人。 pop未返回移除值的主要原因是由于异常安全性。

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

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

这是比我想要做它更温顺,但希望给你一个想法,为什么pop返回一个值是一个坏主意。

+2

这并不能解释为什么标准库中的其他mutator函数确实会按值返回非内建类型(无可否认通常是迭代器,其构造函数不太可能会抛出)。但是,是的,他们没有提供强有力的例外保证并不意味着top/pop不应该。 – 2010-03-05 16:10:21

+0

'template typename C :: value_type pop(C&c){typename C :: value_type copy = c.top(); c.pop();返回副本; }唯一的例外安全问题是如果物品的掷骰子可以投掷,但投掷骰子应该是非常少见的,并且可以很容易地调用非抛掷骰子来调用这个pop(),这与其他容器要求类似。 – 2010-03-06 04:52:11

+0

@Roger:没问题,但是(对于异常安全)编译器不需要实现RVO,在这种情况下,'return copy'会导致复制构造函数被调用(再次)。 (PS投掷dtors是非常糟糕的新闻(tm),因为如果它在堆栈展开期间发生,则会调用std :: terminate,或者出现这种情况,所以可以/应该安全地认为这不应该发生。 – 2010-03-06 05:26:51