C++常见问题解答中类层次结构的打印模式

Printing Pattern for a Class Hierarchy in the C++ FAQ

本文关键字:打印 模式 层次结构 常见 问题解答 C++      更新时间:2023-10-16

我正在为类层次结构实现打印模式,如C++常见问题解答中所述。在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的外观如下FAQ:所示

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返回类型的修订有任何缺陷吗?

如果BaseClass::printOn(stream)返回std::ostream &,则无法写入

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

你必须写:

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

显然,这几乎不比它返回void的情况好。您可以返回一个带有no-op-stream-out运算符的类型:

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<<,并通过static_cast对基类引用调用基类operator<<