跳转到内容

更多 C++ 惯用法/从成员派生

来自 Wikibooks,开放世界中的开放书籍

从成员派生

[编辑 | 编辑源代码]

从派生类的 datamember 初始化基类。

也称为

[编辑 | 编辑源代码]

在 C++ 中,基类在派生类成员之前初始化。这样做是因为派生类成员可以使用对象的部分。因此,所有部分(即所有基类)必须在派生类成员之前初始化。但是,有时需要从仅在派生类中可用的 datamember 初始化基类。这听起来与 C++ 语言的规则相矛盾,因为传递给基类构造函数的参数(派生类成员)必须完全初始化。这会导致循环初始化问题(无限回归)。

以下代码是从 Boost 库 获取的,[1] 显示了这个问题。

#include <streambuf>
#include <ostream>

namespace std {
  class streambuf;
  class ostream {
    explicit ostream(std::streambuf * buf);
    //...
  };
}

// A customization of streambuf.
class fdoutbuf : public std::streambuf
{
public:
    explicit fdoutbuf( int fd );
    //...
};

class fdostream : public std::ostream
{
protected:
    fdoutbuf buf;
public:
    explicit fdostream( int fd )
        : buf( fd ), std::ostream( &buf )
        // This is not allowed: buf can't be initialized before std::ostream.
        // std::ostream needs a std::streambuf object defined inside fdoutbuf.
    {}
};

上面的代码片段展示了程序员想要自定义 std::streambuf 类的情况。他们通过从 std::streambuf 继承在 fdoutbuf 中这样做。fdoutbuf 类用作 fdostream 类的成员,它是一种 std::ostream 类。但是,std::ostream 类需要指向 std::streambuf 类或其派生类的指针。指向 buf 的指针类型是合适的,但是只有在 buf 被初始化的情况下传递它才有意义。但是,除非所有基类都被初始化,否则它不会被初始化。因此产生了无限回归。从成员派生惯用法解决了这个问题。

解决方案和示例代码

[编辑 | 编辑源代码]

这个惯用法利用了基类按声明顺序初始化的事实。派生类控制其基类的顺序,进而控制它们的初始化顺序。在这个惯用法中,添加了一个新类,专门用来初始化导致问题的派生类中的成员。这个新类在所有其他基类之前被引入到基类列表中。由于新类需要完全构造参数的基类之前,它先被初始化,然后引用可以像往常一样被传递。以下是使用从成员派生惯用法进行的解决方案。

#include <streambuf>
#include <ostream>

class fdoutbuf : public std::streambuf
{
public:
    explicit fdoutbuf(int fd);
    //...
};

struct fdostream_pbase // A newly introduced class.
{
    fdoutbuf sbuffer; // The member moved 'up' the hierarchy.
    explicit fdostream_pbase(int fd)
        : sbuffer(fd)
    {}
};

class fdostream
    : protected fdostream_pbase // This class will be initialized before the next one.
    , public std::ostream
{
public:
    explicit fdostream(int fd)
        : fdostream_pbase(fd),   // Initialize the newly added base before std::ostream.
          std::ostream(&sbuffer) //  Now safe to pass the pointer.
    {}
    //...
};

int main()
{
  fdostream standard_out(1);
  standard_out << "Hello, World\n";
  return 0;
}

fdostream_pbase 类是新引入的类,它现在具有 sbuffer 成员。fdostream 类从这个新类继承,并在其基类列表中将其添加到 std::ostream 之前。这确保了 sbufferstd::ostream 之前被初始化,并且指针可以安全地传递给它的构造函数。

已知用途

[编辑 | 编辑源代码]
[编辑 | 编辑源代码]

参考文献

[编辑 | 编辑源代码]
  1. "Boost 实用程序".
华夏公益教科书