非无效功能潜在的缺乏回报

时间:2018-08-04 18:42:12

标签: c++ coding-style

我编写了这段代码,以检查节点是树中的左孩子还是右孩子。但是从清洁的角度来看,我认为它没有正确实施。

我的问题是,如果父指针为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;
}

2 个答案:

答案 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)。