如果不能为空堆栈 return nullptr 如何保护用户?
How to protect user if can't return nullptr for an empty stack?
我正在尝试编写此 Coursera course example of Towers of Hanoi 中给出的 Cube、Stack 示例的实现代码,以学习更多 C++。
在stack.h
中我必须实现:
class Stack {
public:
void push_back(const Cube & cube);
Cube removeTop();
Cube & peekTop();
unsigned size() const;
friend std::ostream & operator<<(std::ostream & os, const Stack & stack);
private:
std::vector<Cube> cubes_;
};
我遇到的问题是 removeTop()
。如果堆栈(向量)为空,我正在考虑 returning nullptr
,因为 pop_back
的行为对于空向量是未定义的。
Calling pop_back on an empty container is undefined. Cpp Reference
inline Cube Stack::removeTop() {
if (!cubes_.empty()) {
Cube top_cube = cubes_.back();
cubes_.pop_back();
return top_cube;
}
else {
return nullptr;
}
}
但是,我在编译过程中遇到错误。
./stack.h:35:12: error: no viable conversion from returned value of type
'std::__1::nullptr_t' to function return type 'uiuc::Cube'
return nullptr;
如果我不能 return nullptr
,我该如何保护用户?我是否仅限于告诉用户不应在空堆栈上调用该函数并让 him/her 负责检查?
可能是这样的:
inline bool Stack::removeTop(Cube& top_cube) {
if (!cubes_.empty()) {
top_cube = cubes_.back();
cubes_.pop_back();
return true;
}
else {
return false;
}
}
基于函数签名,你必须实现,你真的不能。通常,您会用断言来保护这类事情。在某些情况下,您可能会使用 NullObject pattern,或者您可以 return 一个垃圾对象。在较新的 C++ 版本中,您还可以使用 std::optional<T>
.
inline Cube Stack::removeTop() {
if (!cubes_.empty()) {
Cube top_cube = cubes_.back();
cubes_.pop_back();
return top_cube;
}
else {
return Cube {};
}
}
正如 pyj 指出的那样,自 c++17 以来,有一种新机制可以这样做(采用 boost::optional 想法)。这是 std::optional 容器。当使用std::optional时,你告诉这个函数的用户,他可能会得到一个空响应,因此,他必须检查是否初始化。
来自 cpp 参考:
The class template std::optional manages an optional contained value, i.e. a value that may or may not be present.
A common use case for optional is the return value of a function that may fail. As opposed to other approaches, such as std::pair, optional handles expensive-to-construct objects well and is more readable, as the intent is expressed explicitly.
现在开始使用:
inline std::optional<Cube> Stack::removeTop()
{
if (!cubes_.empty())
{
Cube top_cube = cubes_.back();
cubes_.pop_back();
return top_cube;
}
else
{
return std::nullopt;
}
}
而且,当你得到一个 std::optional 而不是 Cube 时,堆上没有 内存分配 ,这比使用指针有很大的优势相同的输出。
If an optional contains a value, the value is guaranteed to be allocated as part of the optional object footprint, i.e. no dynamic memory allocation ever takes place. Thus, an optional object models an object, not a pointer, even though operator*() and operator->() are defined.
这正是异常的用途:
if (cubes_.empty())
throw std::runtime_error("stack underflow");
Cube top_cube = cubes_.back();
cubes_.pop_back();
return top_cube;
用 std::optional
将其复杂化几乎可以肯定 不是 这里的正确答案。试图从空栈中弹出意味着程序迷路了。这应该是一个硬错误,而不是被显示 "you might or might not have this, please check afterwards".
的界面所掩盖的错误
我正在尝试编写此 Coursera course example of Towers of Hanoi 中给出的 Cube、Stack 示例的实现代码,以学习更多 C++。
在stack.h
中我必须实现:
class Stack {
public:
void push_back(const Cube & cube);
Cube removeTop();
Cube & peekTop();
unsigned size() const;
friend std::ostream & operator<<(std::ostream & os, const Stack & stack);
private:
std::vector<Cube> cubes_;
};
我遇到的问题是 removeTop()
。如果堆栈(向量)为空,我正在考虑 returning nullptr
,因为 pop_back
的行为对于空向量是未定义的。
Calling pop_back on an empty container is undefined. Cpp Reference
inline Cube Stack::removeTop() {
if (!cubes_.empty()) {
Cube top_cube = cubes_.back();
cubes_.pop_back();
return top_cube;
}
else {
return nullptr;
}
}
但是,我在编译过程中遇到错误。
./stack.h:35:12: error: no viable conversion from returned value of type
'std::__1::nullptr_t' to function return type 'uiuc::Cube'
return nullptr;
如果我不能 return nullptr
,我该如何保护用户?我是否仅限于告诉用户不应在空堆栈上调用该函数并让 him/her 负责检查?
可能是这样的:
inline bool Stack::removeTop(Cube& top_cube) {
if (!cubes_.empty()) {
top_cube = cubes_.back();
cubes_.pop_back();
return true;
}
else {
return false;
}
}
基于函数签名,你必须实现,你真的不能。通常,您会用断言来保护这类事情。在某些情况下,您可能会使用 NullObject pattern,或者您可以 return 一个垃圾对象。在较新的 C++ 版本中,您还可以使用 std::optional<T>
.
inline Cube Stack::removeTop() {
if (!cubes_.empty()) {
Cube top_cube = cubes_.back();
cubes_.pop_back();
return top_cube;
}
else {
return Cube {};
}
}
正如 pyj 指出的那样,自 c++17 以来,有一种新机制可以这样做(采用 boost::optional 想法)。这是 std::optional 容器。当使用std::optional时,你告诉这个函数的用户,他可能会得到一个空响应,因此,他必须检查是否初始化。
来自 cpp 参考:
The class template std::optional manages an optional contained value, i.e. a value that may or may not be present.
A common use case for optional is the return value of a function that may fail. As opposed to other approaches, such as std::pair, optional handles expensive-to-construct objects well and is more readable, as the intent is expressed explicitly.
现在开始使用:
inline std::optional<Cube> Stack::removeTop()
{
if (!cubes_.empty())
{
Cube top_cube = cubes_.back();
cubes_.pop_back();
return top_cube;
}
else
{
return std::nullopt;
}
}
而且,当你得到一个 std::optional 而不是 Cube 时,堆上没有 内存分配 ,这比使用指针有很大的优势相同的输出。
If an optional contains a value, the value is guaranteed to be allocated as part of the optional object footprint, i.e. no dynamic memory allocation ever takes place. Thus, an optional object models an object, not a pointer, even though operator*() and operator->() are defined.
这正是异常的用途:
if (cubes_.empty())
throw std::runtime_error("stack underflow");
Cube top_cube = cubes_.back();
cubes_.pop_back();
return top_cube;
用 std::optional
将其复杂化几乎可以肯定 不是 这里的正确答案。试图从空栈中弹出意味着程序迷路了。这应该是一个硬错误,而不是被显示 "you might or might not have this, please check afterwards".