template<class T>
T Stack<T>::pop()
{
if (vused_ == 0)
{
throw "Popping empty stack";
}
else
{
T result = v_[used_ - 1];
--vused_;
return result;
}
}
我不明白所有这些,或者说我不理解它,但据说这段代码不起作用,因为它按值返回,我猜它是指结果,并且调用复制构造函数,我不知道这是怎么回事。有人可以解释吗?
答案 0 :(得分:3)
与问题示例中的代码不同,std::stack<T>::pop
不会返回值。
那是因为如果需要复制项目类型并且复制抛出,那么您的操作失败会改变对象的状态,而无法重新建立原始状态。
即。返回值 - pop
不提供 strong exception guarantee (成功或无变化)。
同样,抛出一个文字字符串至少可以说是非常规的。
因此,虽然代码本身没有任何错误(模块可能会输入错误,例如vused_
与v_
等),但它在保证方面很弱,而且非常规可能会导致错误在其他地方的例外处理。
另一种观点认为pop
的非值返回std::stack
不切实际,导致不必要的详细客户端代码。
对于使用堆栈对象,我更喜欢返回值pop
。
但它不是/或:可以根据popped
(状态更改)和pop
轻松定义值返回便捷方法 top
(检查)。这种便捷方法具有较弱的异常保证。但客户端代码程序员可以选择。 : - )
现有设计的改进是支持可移动对象,即替换
<德尔> return result;
德尔>
<德尔>与德尔>
<德尔> return move( result );
德尔>
帮助编译器。
↑更正:
实际上,上面删除的文本具有与预期文本相反的效果,即它禁止RVO(保证构造函数调用)。不知怎的,我的想法在这里颠倒了。但作为一项规则,不要在move
表达式上使用return
,因为默认值是优化,添加的move
可以没有改进的东西,但可以抑制RVO优化。
答案 1 :(得分:2)
是的,按值返回正式调用复制构造函数。但这根本不是问题,因为在实践中,编译器通常能够优化掉额外的副本。这种技术被称为&#34;返回值优化&#34;。
答案 2 :(得分:1)
除了return语句(如果类是可移动但不可复制时可以工作,例如你可以返回std::unique_ptr
),问题就是你在这里做的副本:< / p>
T result = v_[used_ - 1];
要使此副本成为可能,类型T
必须是可复制的(例如T
应该具有公共拷贝构造函数 - 上述语句所需 - 并且复制赋值operator=
)。
作为旁注,抛出一个字符串非常糟糕:你应该抛出一个异常类,例如
throw std::runtime_error("Popping empty stack.");
或者只为这种情况定义一个 ad hoc 类并抛出它,例如:
class StackUnderflowException : public std::runtime_error
{
public:
StackUnderflowException()
: std::runtime_error("Popping empty stack.")
{ }
};
....
throw StackUnderflowException();