All standard stream templates are derived from std::basic_ios. In C++, a virtual base class is initialized by its most derived class; that is, our new odatstream class is responsible for initialization of its base std::basic_ios. Now basic_ios has only one public constructor, which takes a pointer to a stream buffer. This is because basic_ios contains a pointer to the stream buffer, which has to be initialized when a basic_ios subobject is constructed. Consequently, we must figure out how to provide a stream buffer to our base class. Let's consider two options:
Derivation from file stream or string stream classes; that is, class
std::{i,o}fstream or class std::{i,o}stringstream, and
Derivation from std::basic_{i,o}stream.
The file and string stream classes contain a stream buffer data member and already monitor the initialization of their virtual base initialization by providing the pointer to their own stream buffer. If we derive from one of these classes, we do not provide another stream buffer pointer because it would be overwritten by the file or string stream's constructor anyway. (Remember that virtual base classes are constructed before non-virtual base classes regardless of where they appear in the hierarchy.) Consider:
template <class charT, class Traits=std::char_traits<charT> > class MyOfstream : public std::basic_ofstream<charT,Traits> { public: MyOfstream(const char* name) : std::basic_ios<charT,Traits>(...streambufptr...), std::basic_ofstream<charT,Traits>(name) {} // ... };
The order of initialization would be:
std::ios_base()
std::basic_ios (std::basic_streambuf*)
std::basic_ostream (const char*)
std::basic_ofstream (const char*)
MyOfstream (const char*)
In other words, the constructor of basic_ofstream overwrites the stream buffer pointer set by the constructor of basic_ios.
To avoid this dilemma, class basic_ios has a protected default constructor in addition to its public constructor. This default constructor, which requires a stream buffer pointer, doesn't do anything. Instead, there is a protected initialization function std::basic_ios<>::init() that can be called by any class derived from std::basic_ios<>. With this function, initialization of the std::basic_ios<> base class is handled by the stream class that actually provides the stream buffer -- in our example, std::basic_ofstream<>. It calls the protected init() function:
template <class charT, class Traits=std::char_traits<charT> > class MyOfstream : public std::basic_ofstream<charT,Traits> { public: MyOfstream(const char* name) : std::basic_ofstream<charT,Traits>(name) {} // ... };
The order of initialization is:
std::ios_base()
std::basic_ios()
std::basic_ostream (const char*)
calls std::basic_ios::init(basic_streambuf*)
std::basic_ofstream (const char*)
MyOfstream (const char*)
The scheme for deriving from the stream classes is slightly different in that you must always provide a pointer to a stream buffer. This is because the stream classes do not contain a stream buffer, as the file or string stream classes do. For example, a class derived from an output stream could look like this:
template <class charT, class Traits = std::char_traits<charT> > class MyOstream : public std::basic_ostream<charT,Traits> { public: MyOstream(std::basic_streambuf<charT,Traits>* sb) : std::basic_ostream<charT,Traits>(sb) {} // ... };
There are several ways to provide the stream buffer required for constructing such a stream:
Create the stream buffer independently, before the stream is created. Here is a simple example in which a file buffer is created as a separate object and used by the derived stream:
std::filebuf strbuf; strbuf.open("/tmp/xxx"); MyOstream<char> mostr(&strbuf); mostr << "Hello world\n";
Take the stream buffer from another stream. In the example below, the stream buffer is "borrowed" from the standard error stream std::cerr:
MyOstream<char,std::char_traits<char> > mostr(std::cerr.rdbuf()); mostr << "Hello world\n";
Remember that the stream buffer is now shared between mostr and cerr (see Section 34.3 for details).
Contain the stream buffer in the derived stream, either as a data member or inherited. It is typically preferred when a new stream buffer type is used along with the new stream type.