可能重复:
C++ STL stack question: Why does pop() not throw an exception if the stack is empty?
在C ++中设计堆栈时,当堆栈为空时, pop()方法(或 front()方法)应该返回什么?以下哪种设计更好?
好的,我看到我的问题不是那么清楚,让我试着改写它:
有些数据结构可以基于链接列表(如堆栈,队列)实现,并且每个数据结构都有一个返回前端元素(或尾部)的方法。
我想知道,当数据为空时,是否有关于设计这种方法的原则指南。
我对更好的定义是“易于正确使用且难以正确使用”。
答案 0 :(得分:20)
按合同编程的方式是,具有非空堆栈是调用pop
的前提条件,并且调用不满足其前提条件的方法具有未定义结果。我的实现会抛出std::logic_error
,但这不是必需的。在C中,我的实施将abort
通过assert
。
pop
的调用者负责确保在调用pop
之前堆栈不为空的前置保持不变。因此,堆栈应该有一个isEmpty
方法供调用者检查。
答案 1 :(得分:5)
C ++ STL实现不会通过pop()
返回任何内容,因为它解耦返回对象的值并实际从堆栈的内部数据结构中弹出一个对象,使它们成为两个独立的函数。所以这是在设计堆栈数据结构时要考虑的另一种选择。
对于这些类型的数据结构,您的第三个选项也是一种非常惯用的方法。
对于你的第四个选项,而不是一个“唯一的空元素”,我实际上会对你的第三个选项做一个变体,你的pop()
函数接受指针参数而不是引用类型,并返回NULL堆栈中没有任何对象。
答案 2 :(得分:2)
要运行的代码是哪种环境类型?通常情况下,匹配现有的行为范式远比按自己的方式行事要好得多。
当你从空的抽象列表中请求一个元素时,它会抛出异常吗?如果是这样的话,那么弹出非完整堆栈会抛出异常会好得多。
如果定义行为很简单,那么未定义的行为是一个糟糕的选择。
如果大多数代码通过return语句返回项目,那么返回一个控件(bool for if it working)是一个糟糕的设计。如果大多数代码通过参数列表返回项目,那么通过return语句返回一个控件是一个很好的设计,只要对类似集合的其他调用也是如此。
一个空元素没有多大意义,它变成了一个神奇的价值。例如,如果我创建一个列表并在其中推送五个空元素,它是否与没有空元素的列表相同?它是否与包含一个空元素的列表相同?它是一个包含一些元素和空元素的列表吗?空列表是一个“特殊”对象是一回事,但空元素是有问题的,因为它们实际上并不包含元素的行为,它们包含列表的行为。良好的面向对象具有封装在它描述的同一对象中的行为内容。
请注意,空元素与哨兵不同。 Sentinels是集合中包含的实现细节(理想情况下不应该在外部公开)。当我读“返回一个空元素”时,我认为必须非常了解堆栈的实现才能使用它。类之间太多的亲密关系称为紧耦合,它可能使代码更难以修改/修复/更改。
如果您按照自己的方式进行操作,那么您应该尽量使代码的整个方面表现相同。它使维护更容易阅读。
答案 3 :(得分:2)
我可能建议同时使用pop
和trypop
方法。如果失败,pop
只会调用trypop
并抛出异常。我的理由是,对于堆栈的某些用途,当堆栈为空时尝试弹出表示不应发生的程序逻辑错误 - 不平衡的推送/弹出,或由于资源耗尽导致的早期失败错误处理。对于其他用途,弹出失败意味着你已经在输入结束时。当使用带有异常的编程模型时,区分这些用法可以避免在调用空堆栈和抛出异常时使调用者混乱。
答案 4 :(得分:2)
SGI STL implementation of stack有这样的设计说明:
有人可能想知道为什么pop()返回void而不是value_type。那 是的,为什么必须使用top()和pop()来检查和删除顶部 元素,而不是将两者合并在一个成员函数中?在 事实上,这种设计有充分的理由。如果pop()返回了 顶部元素,它必须按值而不是按值返回 reference:引用返回会创建一个悬空指针。返回 然而,按价值来说效率低下:它涉及至少一个冗余 复制构造函数调用。 因为pop()不可能返回一个 价值以既高效又正确的方式,更是如此 明智的做法是不返回任何价值并要求客户 使用top()来检查堆栈顶部的值。
SGI进一步指定了pop():
前提条件:empty()为false。后置条件:size()将是 递减1。
至于top()的行为,SGI指定了这个:
前提条件:empty()为false。
答案 5 :(得分:2)
我会使用选项类型,也许是Boost.Optional。它专门为可选的返回值提供支持,这正是你想要的。