Non-void 潜在功能不足 return
Non-void function potential lack of return
我写了这段代码来检查一个节点是树中的左节点还是右节点 child。但是从清洁的角度来看,我认为它没有得到妥善实施。
我的问题是,如果 parent 指针为空,return 设置默认值如 false 是危险的,因为 false 意味着它是正确的 child。然而,如果我想让它在没有警告的情况下编译,这个函数需要一个 return 。作为解决方案,空投绝对是丑陋的。
我看到人们使用 assert(parent)
而不是 if
,但我认为这也不好,因为它断言了调试功能。
简而言之,实现这个功能有什么更好的想法?
bool isLeftChild() const {
if(parent){
if(this == (parent->left).get()){
return true;
}
else if(this == (parent->right).get()){
return false;
}
}
else throw;
}
我想这取决于你想要什么。我认为一个名为 isLeftChild()
的函数应该 return false
以防节点不是任何人的 child,因为如果它不是任何人的 child,它肯定不是左边 child…
关于 assert()
的使用,您应该问自己一个问题:是否有必要保证 well-defined 在函数被错误调用的情况下的行为? assert()
的要点是捕捉正确程序中不应该出现的情况。一旦构建了发布版本,就不再需要这些检查了,因为它们检查的情况可能(应该)永远不会发生。另外,请注意,没有参数的 throw
表达式将简单地调用 std::terminate()
,除非正在处理活动异常。我怀疑 isLeftChild()
只能从 catch
块内部调用!?因此,要让这个 throw
执行您最有可能想要执行的操作,您需要抛出 某些内容 ,例如 std::logic_error
.
清洁度终究是见仁见智。你遇到的问题是这个函数的契约是什么。也就是说,如果在具有 NULL 父节点的节点上调用此代码,您是否希望它定义此代码的行为?
如果答案是肯定的,那么投掷是完全有效的。很像 vector::at
如果索引超出范围就会抛出。
bool isLeftChild() const
{
if(!parent) throw ...;
if(this == (parent->left).get()){
return true;
}
else if(this == (parent->right).get()){
return false;
}
}
如果答案是否定的,那么你不应该扔。这就是 vector::operator[]
处理越界索引的方式。在 C++20 中,您可以将其表示为合同:
bool isLeftChild() const
[[expects: parent != nullptr]]
{
if(this == (parent->left).get()){
return true;
}
else if(this == (parent->right).get()){
return false;
}
}
但这在概念上是一个断言;你是说如果你在一个父节点为 NULL 的节点上调用这个函数会导致未定义的行为。
哪个 "cleaner" 取决于您以及您希望界面如何运行。 "Wide contracts"(你抛出失败而不是调用 UB)通常被视为 "safer" 的替代方案。这主要是因为您会从出错的地方而不是其他位置抛出异常。但在这种特殊情况下,如果 parent
为 NULL,当您尝试取消引用它时,您会立即发现。
但即便如此,宽契约在现代 C++ 中通常被认为是糟糕的形式。事实上,对于 C++20 中的契约,委员会正在研究慢慢删除标准库中抛出 logic_error
s 的地方,将它们变成契约违规(即:UB)。
我写了这段代码来检查一个节点是树中的左节点还是右节点 child。但是从清洁的角度来看,我认为它没有得到妥善实施。
我的问题是,如果 parent 指针为空,return 设置默认值如 false 是危险的,因为 false 意味着它是正确的 child。然而,如果我想让它在没有警告的情况下编译,这个函数需要一个 return 。作为解决方案,空投绝对是丑陋的。
我看到人们使用 assert(parent)
而不是 if
,但我认为这也不好,因为它断言了调试功能。
简而言之,实现这个功能有什么更好的想法?
bool isLeftChild() const {
if(parent){
if(this == (parent->left).get()){
return true;
}
else if(this == (parent->right).get()){
return false;
}
}
else throw;
}
我想这取决于你想要什么。我认为一个名为 isLeftChild()
的函数应该 return false
以防节点不是任何人的 child,因为如果它不是任何人的 child,它肯定不是左边 child…
关于 assert()
的使用,您应该问自己一个问题:是否有必要保证 well-defined 在函数被错误调用的情况下的行为? assert()
的要点是捕捉正确程序中不应该出现的情况。一旦构建了发布版本,就不再需要这些检查了,因为它们检查的情况可能(应该)永远不会发生。另外,请注意,没有参数的 throw
表达式将简单地调用 std::terminate()
,除非正在处理活动异常。我怀疑 isLeftChild()
只能从 catch
块内部调用!?因此,要让这个 throw
执行您最有可能想要执行的操作,您需要抛出 某些内容 ,例如 std::logic_error
.
清洁度终究是见仁见智。你遇到的问题是这个函数的契约是什么。也就是说,如果在具有 NULL 父节点的节点上调用此代码,您是否希望它定义此代码的行为?
如果答案是肯定的,那么投掷是完全有效的。很像 vector::at
如果索引超出范围就会抛出。
bool isLeftChild() const
{
if(!parent) throw ...;
if(this == (parent->left).get()){
return true;
}
else if(this == (parent->right).get()){
return false;
}
}
如果答案是否定的,那么你不应该扔。这就是 vector::operator[]
处理越界索引的方式。在 C++20 中,您可以将其表示为合同:
bool isLeftChild() const
[[expects: parent != nullptr]]
{
if(this == (parent->left).get()){
return true;
}
else if(this == (parent->right).get()){
return false;
}
}
但这在概念上是一个断言;你是说如果你在一个父节点为 NULL 的节点上调用这个函数会导致未定义的行为。
哪个 "cleaner" 取决于您以及您希望界面如何运行。 "Wide contracts"(你抛出失败而不是调用 UB)通常被视为 "safer" 的替代方案。这主要是因为您会从出错的地方而不是其他位置抛出异常。但在这种特殊情况下,如果 parent
为 NULL,当您尝试取消引用它时,您会立即发现。
但即便如此,宽契约在现代 C++ 中通常被认为是糟糕的形式。事实上,对于 C++20 中的契约,委员会正在研究慢慢删除标准库中抛出 logic_error
s 的地方,将它们变成契约违规(即:UB)。