更多 C++ 惯用法/从成员派生
从派生类的 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
之前。这确保了 sbuffer
在 std::ostream
之前被初始化,并且指针可以安全地传递给它的构造函数。