【问题标题】:Printing Pattern for a Class Hierarchy in the C++ FAQC++ FAQ 中类层次结构的打印模式
【发布时间】:2013-02-14 18:58:38
【问题描述】:

我正在实现printing pattern for a class hierarchy,如C++ FAQ 中所述。在FAQ中,打印函数在基类中声明如下:

protected:
  virtual void printOn(std::ostream& o) const = 0;  // pure virtual
  // -- or --
  virtual void printOn(std::ostream& o) const;      // plain virtual

我正在考虑实现printOn 方法的纯虚拟版本,但有一些变化。我想将返回类型从void 更改为std::ostream&,如下所示:

protected:
  virtual std::ostream& printOn(std::ostream& o) const;

我认为这种方法的优点是允许使用链接方法更轻松地将基类的输出合并到派生类的输出中。这是一个例子:

std::ostream& DerivedClass::printOn(std::ostream& stream) const
{
  return stream
      << "<DerivedClass>" << '\n'
      << BaseClass::printOn(stream)
      << "<member_one>" << member_one_ << "</member_one>" << '\n'
      << "<member_two>" << member_two_ << "</member_two>" << '\n'
      << "</DerivedClass>" << std::endl;
}

相比之下,如果虚拟BaseClass::printOn 方法被声明为void,则下面是DerivedClass::printOn 的外观,如常见问题解答中所示:

void DerivedClass::printOn(std::ostream& stream) const
{
    stream << "<DerivedClass>" << '\n';
    BaseClass::printOn(stream);

    stream
        << "<member_one>" << member_one_ << "</member_one>" << '\n'
        << "<member_two>" << member_two_ << "</member_two>" << '\n'
        << "</DerivedClass>" << std::endl;
}

 

问题:有没有人发现我对printOn返回类型的修改有任何缺陷?

【问题讨论】:

    标签: c++ printing iostream hierarchy


    【解决方案1】:

    如果BaseClass::printOn(stream) 返回一个std::ostream &amp; 那么你不能写

    stream << ... << BaseClass::printOn(stream) << ...;
    

    你必须写:

    stream << ...;
    BaseClass::printOn(stream) << ...;
    

    显然,这比返回void 的情况好不了多少。您可以返回带有无操作流输出运算符的类型:

    struct noop_manipulator {
      noop_manipulator(std::ostream &) {}
      friend inline std::ostream &operator<<(std::ostream &os, const noop_manipulator &) {
        return os;
      }
    };
    
    noop_manipulator DerivedClass::printOn(std::ostream& stream) const
    {
        return stream << ...;
    }
    

    或者,您可以只玩语法:

    void DerivedClass::printOn(std::ostream& stream) const
    {
        return stream << "<DerivedClass>" << '\n',
            BaseClass::printOn(stream), stream
            << "<member_one>" << member_one_ << "</member_one>" << '\n'
            << "<member_two>" << member_two_ << "</member_two>" << '\n'
            << "</DerivedClass>" << std::endl;
    }
    

    所有这些方法的缺点是它们掩盖了您对超类虚拟调用的使用,这是非常不寻常的,因此应该尽可能清楚地说明。所描述的印刷图案本身就很不寻常;除非您已经拥有虚拟继承层次结构,否则派生类更常见的是拥有自己的operator&lt;&lt;,并通过static_cast 调用基类operator&lt;&lt; 来引用基类。

    【讨论】:

    • @DavidRR 从语法上讲,您调用的是ostream &lt;&lt; ostream,它没有特别定义,但通常会调用operator void * 失败检查运算符。您应该会在输出流中看到一个指针值。
    • 是的,在我实际制作了我所提议的原型之后,我确实很清楚我最终调用了ostream &lt;&lt; ostream。而且我现在确实正在从头开始开发虚拟继承层次结构,我觉得 C++ FAQ 推荐的打印模式非常合适。我还会记住您推荐的替代方法,以在派生类中覆盖 operator&lt;&lt;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-14
    • 1970-01-01
    • 1970-01-01
    • 2011-05-19
    • 2017-09-25
    • 1970-01-01
    相关资源
    最近更新 更多