简体   繁体   中英

Why the constructor of std::ostream is protected?

Why I can't just create an "empty" stream for my output like this

std::ostream out;

?

This rows is apparently illegal with both clang 3.4 and gcc 4.8.1 under linux with libstdc++ , I really don't get why, I mean why I can't just create a stream out of nowhere and use it like I want to ? notice that std::ofstream out; is 100% ok instead. I just don't get the logic behind this. This is even stranger if you consider that after the creation I can just use this buffer and share a common buffer with other buffers with copyfmt so there is no real need for my std::ostream to be initialized to something right from the creation of the object to be useful.

Please don't deviate from this, I don't need stringstreams , I need ios streams because of what I have to do and because of the methods and the properties that they are offering.

I'll admit that I don't understand it either. I can't find any default constructor at all for std::istream , and I would think you would want one if you want to create a bidirectional stream, because of the strange way std::ios_base works: the constructor does not initialize anything, but the derived class must call std::ios_base::init explicitly in its constructor. When multiple inheritance is involved (ie bidirectional IO, where the class derives from both std::istream and std::ostream ), I would expect only the most derived class to call std::ios_base::init . (In std::iostream , std::ios_base::init will be called twice.) In fact, before looking it up in the standard, I was about to answer that the default constructor was protected, because it didn't call std::ios_base::init , and using it directly, rather than in a derived class, would result in an uninitialized stream.

Anyhow, your immediate problem has a simple solution:

std::ostream out( NULL );

Also: the function you need to set up its sink later is the non-const version of rdbuf() , not copyfmt() . rdbuf() is used to read and to set the pointer to the streambuf , copyfmt() copies the formatting flags, but does not touch the pointer to streambuf .

So you can do things like:

std::ostream out( NULL );
//  ...
std::filebuf fileBuffer;
if ( filenameGiven ) {
    fileBuffer.open( filename.c_str(), std::ios_base::out );
}
if ( fileIsOpen() ) {
    out.rdbuf( &fileBuffer );
} else {
    out.rdbuf( std::cout.rdbuf() );
}

(I do this a lot. In fact, I thought that it was the usual idiom when you didn't know up front whether to output to a file or to std::cout .)

EDIT:

And yet another correction: the non-const version of rdbuf calls clear() , so you don't have to. (I knew I'd done this without calling clear() , but when I saw that init set badbit ...)

Anyhow: the summary is: it's usually preferrable to pass a pointer to a valid streambuf to the constructor of std::ostream , but if you can't, it's perfectly valid to pass a null pointer, and set a valid pointer later using rdbuf() . And the answers which say otherwise are simply wrong.

std::ostream is conceptually abstract - you're not supposed to create them (but see other answers for more detail on that).

std::stringstream gives you everything std::ostream does because it derives from it. This also means that anywhere you want an std::ostream& , for example in function arguments, you can pass a std::stringstream object.

You say "there's no need for [it] to be initialised [...] to be useful". That makes no sense at all. You cannot use anything that hasn't been initialised: even int s.

The default constructor of std::basic_ostream is protected because it generally doesn't make any sense to create an std::basic_ostream without setting its std::basic_streambuf and the default constructor actually doesn't do any initialization (see below). There is, however, another constructor taking a stream buffer as argument. If you really need an std::ostream which isn't set up to do anything, yet, you can use that constructor:

std::ostream out(0);

This std::ostream will have the std::ios_base::badbit set until a stream buffer is set up using std::ios::rdbuf() .

The underlying reason for std::ostream 's default constructor being protected is that it actually deliberately doesn't do anything useful! In particular, calling this constructor [from a further derived class] will not initialize the stream buffer at all: the standard doesn't mandate any behavior explicitly, ie, the default constructor has implicitly the behavior of the generate default constructor (or, in C++2011 as if it is defined using = default ). To get the stream buffer initialized, it would need to call std::ios::init() . The reason for this odd behavior is that constructing an object of the further derived class std::iostream would initialized the std::ios object twice, once through std::ostream and once through std::istream .

Unlike some of the other answers suggest, std::ostream isn't abstract at all. In fact, it only has exactly one virtual function anyway and that is its destructor (the destructor being virtual happens to be my fault; I'm not entirely convinced that it was really a good idea to force that).

stringstream is an ios stream.

But about your question:

An ostream writes somewhere. if you declare

std::ostream out;
out << "Hello, world!"<<std::endl

the "Hello, world!" should be written somewhere. But where? That depends on the implementation for each specific ostream . Yes, there is a buffer, but that buffer also depends on the specific implementation.

So when you say "I want an ostream " I have to ask - one which writes stuff... to where?

Given your answer, that will tell me which specific instance/implementation of ostream you need to use.

The std::ostream is a generalized stream class. Because of that it has no way of knowing what it is streaming to, which is the whole point of having a generalized class.

When you send data to a stream it doesn't actually store the data itself. It simply passes it forward to an associated buffer. Taking this into consideration, creating a generalized stream without assigning this buffer to it makes no sense, as the generalized class cannot create an empty generalized buffer. (The buffer itself needs to be a buffer of a concrete type, which is also understood by looking at the generalized buffer std::treambuf that doesn't have a public consstructor at all)

Compare this with the std::ofstream, which is a stream with a specific type of buffer, it is now possible for the ofstream to know what kind of buffer it uses, thus being able to instantiate a default std::filebuf.

To solve your concrete problem.

Create the buffer of your wanted type first, and then create the a generalized std::ostream with the buffer as a parameter. You can then at a later point connect to a file using std::filebuf::open().

Example:

std::filebuf fileBuffer;
std::ostream myOstream(&fileBuffer); // Hand over the address of the fileBuffer

fileBuffer.open("filename.txt", std::ios::out);
myOstream << "Text to file";

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM