我编写了这段代码,以检查节点是树中的左孩子还是右孩子。但是从清洁的角度来看,我认为它没有正确实施。
我的问题是,如果父指针为null,则返回类似false的默认值很危险,因为false表示它是一个正确的孩子。但是,如果我希望它在没有警告的情况下进行编译,则需要返回此函数。解决这个问题的方法绝对是丑陋的。
我已经看到人们使用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;
}
答案 0 :(得分:1)
我想这取决于您想要什么。我认为,如果节点不是任何人的孩子,则称为isLeftChild()
的函数应返回false
,因为如果它不是任何人的孩子,则肯定不是左孩子…
关于assert()
的使用,您应该问自己一个问题:是否有必要在错误调用函数的情况下保证行为明确? assert()
的重点是捕获在正确的程序中永远不会发生的情况。一旦构建了发行版本,就不再需要进行这些检查,因为它们检查的情况可能(应该)永远不会发生。另外,请注意,没有参数的throw
表达式将仅调用std::terminate()
,除非正在处理活动异常。我怀疑isLeftChild()
仅打算从catch
块内部调用!?因此,为了使throw
能够完成您最有可能要做的事情,您需要抛出某物,例如std::logic_error
。
答案 1 :(得分:1)
清洁度最终取决于意见。您遇到的问题是此功能的合同是什么问题。也就是说,如果在具有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父级的节点上调用此函数,则会导致未定义的行为。
哪个“清洁”取决于您以及您希望界面如何运行。 “宽合同”(您引发失败而不是调用UB)通常被视为“更安全”的选择。这主要是因为您从出错的地方而不是其他位置抛出了异常。但是在这种特殊情况下,如果parent
为NULL,则在尝试取消引用时会立即发现。
但是即使如此,在现代C ++中,广泛的合同通常被认为是较差的形式。确实,对于使用C ++ 20的合同,委员会正在研究慢慢删除标准库中引发logic_error
的位置,而将它们变成违反合同的行为(即UB)。