【问题标题】:One-line output operator for a binary tree二叉树的单行输出运算符
【发布时间】:2019-02-18 14:54:25
【问题描述】:

我用 C++ 编写了一个简单的二叉树类,并想向它添加一个输出运算符。我的第一次尝试是:

ostream& operator<<(ostream& out, const Tree& tree) {
    out << tree.myData;
    if (tree.myLeft)
        out << "(" << (*tree.myLeft)  << ")";
    if (tree.myRight)
        out << "[" << (*tree.myRight)  << "]";
    return out;
}

(其中 myLeft 和 myRight 分别是指向当前树的左右子节点的指针)。这可以正常工作,但是它不够酷,因为它跨越了几行并且需要多次写入“out

为了尝试创建一个单行运算符,我写了这个:

ostream& operator<<(ostream& out, const Tree& tree) {
    return (out << tree.myData
        << "(" << (tree.myLeft? *tree.myLeft: "")  << ")"
        << "[" << (tree.myRight? *tree.myRight: "") << "]");
}

但是,这会产生错误:

不兼容的操作数类型('Tree' 和 'const char [1]')

所以我尝试了这个:

ostream& operator<<(ostream& out, const Tree& tree) {
    return (&tree?
        out << tree.myData
            << "(" << *(tree.myLeft)  << ")"
            << "[" << *(tree.myRight) << "]":
        out);
}

这适用于我的计算机,但会生成一条警告,暗示这是未定义的行为:

在定义良好的 C++ 代码中,引用不能绑定到取消引用的空指针;可以假定指针总是转换为真 [-Wundefined-bool-conversion]

问题:有没有办法在一个简单的语句中编写这个输出运算符?

【问题讨论】:

  • This works correctly, however, it is not sufficiently cool, since it spans several lines and requires to write "out &lt;&lt; " several times. 为什么这很重要?您只需编写一次函数,然后只需在实际代码中执行out &lt;&lt; Tree;
  • 我问你的问题:你为什么要做这样的事情?你的第一个代码块清晰、简洁、易于维护,几乎不需要脑力就能理解。这就是你应该默认编写的那种代码。
  • @NathanOliver 这不是用于生产的代码 - 它是用于学习的。由于我尝试编写单行代码,我已经了解了关于 C++ 的两个新内容:三元运算符需要兼容的数据类型,以及取消引用空指针被定义为未定义的行为。两者都让我感到非常惊讶。现在,我很好奇 c++ 是否还有另一个惊喜可以解决这个难题。

标签: c++ output binary-tree


【解决方案1】:

一个简单而优雅的解决方案是重新设计您的树以在没有空指针的情况下工作。相反,将当前使用的空指针替换为指向具有与空树一致的行为的哨兵树节点的指针。

然后你可以重写你的输出流操作符如下:

ostream& operator<<(ostream& out, const Tree& tree) {
    if (&tree == &Tree::NULL_TREE_SENTINEL) return out;
    return out << tree.myData
        << "(" << *tree.myLeft  << ")"
        << "[" << *tree.myRight << "]";
}

(假设Tree 内部有一个对应的静态成员,标记是指向该成员的指针,就像单例一样。)

或者,哨兵树节点可以是具有这种行为的Tree 的子类的实例。这有时被称为null object pattern。但是,它需要动态调度(即通过虚拟成员函数的运行时多态性)才能工作。


除此之外,您并不能完全正确地诊断出第二个代码的问题:

这适用于我的电脑

看起来可以工作,但实际上并没有。我不知道在什么情况下该代码实际上会做一些讨厌的事情。但为了清楚起见,由于子表达式*(tree.myLeft)*(tree.myRight),您的代码是非法的:这些表达式正在取消引用空指针,这绝不是合法的。您收到的关于 &amp;tree 测试的警告消息只是先前错误的症状

【讨论】:

  • 好主意。但它返回一个错误“错误:二进制表达式的无效操作数('const Tree'和'Tree')”。当我比较指针时问题解决了:“if (&tree == &Tree::NULL_TREE_SENTINEL)...”“
  • 关于第二条评论:确实我花了一些时间才意识到这是非法的。我一直认为引用只是指针的语法糖,“&*p”等价于“p”。现在我看到当 p 为 null 时,“&*p”不等同于“p”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-21
  • 1970-01-01
  • 2019-08-09
  • 1970-01-01
  • 2011-04-06
  • 2023-03-07
  • 1970-01-01
相关资源
最近更新 更多