为什么pop()会参与争论?

时间:2010-05-03 05:12:31

标签: java c++ stack

快速背景
我是一名Java开发人员,在我的免费/无聊时间里一直在玩C ++。

前言
在C ++中,您经常会看到pop通过引用参数:

void pop(Item& removed);

我知道用你删除的内容“填写”参数会很好。这对我来说完全有意义。这样,要求删除顶部项目的人可以查看删除的内容。

但是,如果我在Java中这样做,我会做这样的事情:

Item pop() throws StackException;

这样,在pop之后我们返回:结果为NULL,一个Item或一个异常将被抛出。

我的C ++教科书向我展示了上面的例子,但我看到很多堆栈实现没有参数(例如stl stack)。

问题
应该如何在C ++中实现pop函数?

奖金
为什么呢?

5 个答案:

答案 0 :(得分:26)

回答这个问题:你不应该在C ++中实现pop函数,因为它已经由STL实现了。 std::stack容器适配器提供方法top以获取对堆栈顶部元素的引用,并使用方法pop来移除顶部元素。请注意,仅pop方法不能用于执行这两个操作,正如您所询问的那样。

为什么要这样做?

  1. 异常安全:Herb Sutter在GotW #82中对此问题做了很好的解释。
  2. 单一责任原则:在GotW#82中也有提及。 top负责一项责任,pop负责另一项责任。
  3. 不要为不需要的东西付费:对于某些代码,可能只需要检查顶部元素然后弹出它,而不必制作一个(可能很昂贵的)副本元件。 (SGI STL文档中提到了这一点。)
  4. 任何希望获取元素副本的代码都可以免费获得:

    Foo f(s.top());
    s.pop();
    

    此外,this discussion可能很有趣。

    如果要实现pop来返回值,那么无论是按值返回还是将其写入out参数都无关紧要。大多数编译器实现RVO,它将优化按值返回的方法,与copy-into-out-parameter方法一样高效。请记住,其中任何一个都可能比使用top()或front()检查对象效率低,因为在这种情况下绝对没有复制完成。

答案 1 :(得分:4)

Java方法的问题是它的pop()方法至少有两个影响:删除一个元素,然后返回一个元素。这违反了软件设计的单一责任原则,这又为设计复杂性和其他问题打开了大门。它还意味着性能损失。

在STL方式中,有时候你pop()对弹出的项目不感兴趣。你只想要删除顶部元素的效果。如果函数返回元素并忽略它,那么这就是浪费的副本。

如果你提供两个重载,一个接受引用而另一个不引用,那么你允许用户选择他(或她)是否对返回的元素感兴趣。通话的表现最佳。

STL不会使pop()函数重载,而是将它们分成两个函数:back()(或top()适配器的std::stack)和{ {1}}。 pop()函数只返回元素,而back()函数只删除它。

答案 2 :(得分:1)

使用C ++ 0x会使整个事情再次变得困难。

作为

stack.pop(item); // move top data to item without copying

可以有效地从堆栈中移动顶部元素。而

item = stack.top(); // make a copy of the top element
stack.pop(); // delete top element

不允许这样的优化。

答案 3 :(得分:0)

我可以在C ++中看到使用此语法的唯一原因:

void pop(Item& removed);

如果您担心发生不必要的副本。

如果您返回Item,则可能需要该对象的其他副本,可能贵。

实际上,C ++编译器非常擅长复制省略,并且几乎总是实现返回值优化(通常即使在禁用优化的情况下进行编译),这使得这一点没有实际意义,甚至可能意味着简单的“按值返回”在某些情况下,版本变得更快

但是如果你进入过早优化(如果你担心)编译器可能不会优化副本,即使在实践中它做到这一点) ,您可以通过分配参考参数来争辩“返回”参数。

更多信息here

答案 4 :(得分:0)

IMO,C ++中Java pop函数等效的良好签名就像:

boost::optional<Item> pop();

使用选项类型是返回可能可用或不可用的内容的最佳方式。