当堆栈为空时,’pop()’方法应该返回什么?

可能重复:
C ++ STL堆栈问题:为什么如果堆栈为空,pop()不会抛出exception?

在C ++中设计堆栈时,当堆栈为空时, pop()方法(或front()方法)应该返回什么? 以下哪种设计更好?

  1. 抛出exception
  2. 未定义,但要求用户在调用pop()之前调用isempty()方法进行检查
  3. 返回bool代码,同时使用额外参数(引用)传递弹出元素
  4. 定义一个唯一的空元素

好的,我看到我的问题不是那么清楚,让我试着改写它:

有一些数据结构可以基于链表,如堆栈,队列来实现,并且每个数据结构都有一个返回前端元素(或尾部)的方法。

我想知道,关于数据为空时的情况设计这样的方法是否有任何原则指导。

而我对更好的定义是“易于正确使用且难以正确使用”。

按合同编程的方式是,具有非空堆栈是调用pop前提条件 ,并且调用不满足其前提条件的方法具有未定义的结果。 我的实现会抛出一个std::logic_error ,但这不是必需的 。 在C中,我的实现将通过assert abort

pop的调用者负责确保在调用pop之前堆栈不为空的优先级。 因此,堆栈应该有一个isEmpty方法供调用者检查。

C ++ STL实现不会通过pop()返回任何内容,因为它解耦返回对象的值并实际从堆栈的内部数据结构中弹出一个对象,使它们成为两个独立的函数。 因此,在设计堆栈数据结构时,这是另一个选择。

对于这些类型的数据结构,您的第三种选择也是一种非常惯用的方法。

对于你的第四个选项,而不是一个“唯一的空元素”,我实际上会对你的第三个选项做一个变化,你的pop()函数接受指针参数而不是引用类型,如果没有剩下的对象则返回NULL在堆栈中。

要运行的代码是哪种环境类型? 通常情况下,匹配现有的行为范式要比按照自己的方式行事要好得多。

当你从空的抽象列表中请求一个元素时,它是否会引发exception? 如果是这样,那么弹出非完整堆栈抛出exception会好得多。

如果定义行为很简单,那么未定义的行为是一个糟糕的选择。

如果大多数代码通过return语句返回项目,那么返回一个控件(bool for if it working)是一个糟糕的设计。 如果大多数代码通过参数列表返回项目,那么通过return语句返回控件是一个很好的设计,前提是对类似集合的其他调用也是如此。

空元素没有多大意义,它变成了一个神奇的价值。 例如,如果我创建一个列表并在其中推送五个空元素,它是否与没有空元素的列表相同? 它是否与包含一个空元素的列表相同? 它是一个包含一些元素和空元素的列表吗? 空列表是一个“特殊”对象是一回事,但空元素是有问题的,因为它们并不真正包含元素的行为,它们包含列表的行为。 良好的面向对象具有封装在其描述的同一对象中的行为的内容。

请注意,空元素与标记不同。 Sentinels是集合中包含的实现细节(理想情况下不应该在外部公开)。 当我读“返回一个空元素”时,我认为必须非常了解堆栈的实现才能使用它。 类之间太多的亲密关系称为紧耦合,它可能使代码更难以修改/修复/更改。

如果你按照自己的方式进行操作,那么你应该尽可能地使代码的整个方面表现相同。 它使维护更容易阅读。

我可能会建议同时使用poptrypop方法。 如果失败, pop会简单地调用trypop并抛出exception。 我的理由是,对于堆栈的某些用途,当堆栈为空时尝试弹出表示不应发生的程序逻辑错误 – 不平衡的推送/弹出,或由于资源耗尽而导致的早期失败error handling。 对于其他用途,弹出失败意味着您已经在输入结束时。 使用带有exception的编程模型时,区分这些用法可以避免在调用空堆栈和抛出exception时使调用者混乱。

堆栈的SGI STL实现具有以下设计说明:

有人可能想知道为什么pop()返回void而不是value_type。 也就是说,为什么必须使用top()和pop()来检查和删除顶部元素,而不是将两者合并在一个成员函数中? 事实上,这种设计有充分的理由。 如果pop()返回顶部元素,则它必须按值而不是按引用返回:按引用返回将创建一个悬空指针。 然而,按值返回是低效的:它涉及至少一个冗余的复制构造函数调用。 由于pop()不可能以高效和正确的方式返回值,因此根本不返回任何值并要求客户端使用top()来检查值是更明智的堆栈的顶部。

SGI进一步指定pop():

前提条件:empty()为false。 后置条件:size()将减1。

至于top()的行为,SGI指定了这个:

前提条件:empty()为false。

我会使用一个选项类型,也许是Boost.Optional 。 它专门为可选的返回值提供支持,这正是您想要的。