Why the streams in C++?
As you all know there are libraries using streams such as iostream
and fstream
.
My question is:
- Why streams? Why didn't they stick with functions similar to
print
,fgets
and so on (for example)?
They require their own operators <<
and >>
but all they do could be implemented in simple functions like above, also the function
printf开发者_StackOverflow中文版("Hello World!");
is a lot more readable and logical to me than
cout << "Hello World";
I also think that all of those string abstractions in C++ all compile down to (less efficient) standard function calls in binary.
Streams have better type safety.
For instance printf("%s", a);
can go horribly wrong if a
is an integer. cout << a;
doesn't have this problem.
Another issue is that streams better conform to Object Oriented design methodologies.
For instance you have a simple application that writes some output and then you want the output to go to a file instead of to the console. With C calls you'll have to replace all of the calls to printf
to calls to fprintf
and take care to maintain the FILE*
along the way. With streams you just change the concrete class of the stream you're using and that's it, most of the code remains the same like so:
void doSomething(ostream& output)
{
output << "this and that" << a;
}
doSomething(cout);
doSomething(ofstream("c:\file.txt"));
For one, it allows you to take advantage of the C++ object model to create functions that do not care whether they are writing to standard output, a file, or a network socket (if you have a network socket that derives from ostream
). E.g.
void outputFoo(std::ostream& os)
{
os << "Foo!";
}
int main()
{
outputFoo(std::cout);
std::ofstream outputFile("foo.txt");
outputFoo(outputFile);
MyNetworkStream outputSocket;
outputFoo(outputSocket);
}
And similarly for input streams.
Streams also have an advantage when it comes to inputting and outputting objects. What would happen if you wanted to read in an object with scanf
?
MyObject obj;
scanf("??", &obj); // What specifier to use?
Even if there were an appropriate specifier, how would scanf
know how to fill in the members of the object? With C++ streams, you can overload operator<<
and write
MyObject obj;
std::cin >> obj;
And it will work. Similarly for std::cout << obj
, so you can write the object serialization code in one place and not have to worry about it anywhere else.
C++ streams are type safe. In C, if you say:
double d = 1.23;
printf( "%d", d );
you are not guaranteed to get an error message, even though the conversion is incorrect.
You seem to be asking very basic C++ questions. Which C++ text book are you using that doesn't cover them?
printf is not type safe for one. The cout interface is also more generic, which allows a whole lot of things unavailable with the printf version. One great example is that your stream operator for your types is, if you're doing it right, refering to std::ostream as the stream, not cout. Thus you can change the destination of the output simply by using a different stream. To do that with printf you have to do a whole lot of platform dependent overwriting of the output handles.
I also think that all of those string abstractions in C++ all compile down to (less efficient) standard function calls in binary.
Think all you want. Until you actually test your assumption your opinion won't hold much weight. Furthermore you need to consider the loss of abstraction, something that is usually a whole lot more important in modern software development.
Besides being type safe and polymorphic, streams are more portable. On some systems printf with a long requires "%d" and on some systems it requires "%ld".
Streams can be chained together
cout << "hello" << " " << "world"
Another benefit of streams is that they were made to be extendable. With streams, you can have your user defined classes work just like built in types:
class foo { ... };
ostream &operator<<(ostream &ostr, const foo &f)
{
ostr << ... how you want to print a foo ...;
return ostr;
}
And now you can print a foo just like anything else:
int n = ...
foo f = ...
cout << n << ": " << f << endl;
C++ IOStreams are ridiculously inefficient (in most implementations I know of). Often, that is not a concern, but when it is, the library is basically useless. It's also a good point that the syntax is unintuitive (and very, very verbose). The library is complex and needlessly hard to extend. It's not very flexible either. When contrasted against something like the STL, IOStreams really looks like a bad dream. But it's here, and we're stuck with it.
The reason it's here, and the reason it looks like it does, is that it was developed earlier, before C++ was a mature language. Before we had decades of experience telling us what is, and is not, good library design. Before anyone really knew what the options were.
C++ needed an I/O library that was better than C's. And in some important ways, C++ IOStreams are better. They're type-safe and extensible as others have mentioned. By implementing a single operator, I can print out a user-defined class. That couldn't be done with printf
. I also don't have to worry about getting format specifiers wrong and printing out garbage because of a lack of type safety.
Those things needed to be fixed. And hey, in the early days, virtual functions and operator overloading were the shit. It looked cool. Of course libraries wanted to use those features.
The IOStreams library is a compromise between:
- something safer and more extensible than C's
stdio.h
- something efficient
- something well-designed and intuitive
- a library that actually existed at the time when C++ was being standardized. (they had to add something, so they had to choose between the candidates that actually existed at the time.)
The library doesn't achieve all of these, and I believe that today, with our decades of experience with the language, we could have designed a far better library. But back in the mid-90's, when they were looking for an I/O library to add, this was the best they could find.
The stream like syntax with operator<<
and operator>>
allows nice concatenation.
printf("%d%d%d%d%d", a, b, c, d, e);
vs
cout << a << b << c << d << e;
Moreover in the first approach one must always think about the type, while in the stream approach, the type must not be specified.
C++ lets you use printf, by the way. So if you like that, go ahead and use it.
Streams are used for a lot more than writing output to the console. They're a general purpose data buffering solution that can be applied to everything from screen output to file handling to network traffic and input device interfaces. Your stuff can write to (or read from) streams without caring where that data is actually going to go.
Say you have a complex object that you want to be able to write to the output console, to a log file, and into a debugging window popup. Are you going to write three functions that do almost the exact same thing, or are you going to write one function to which you pass an(y) output stream?
Actually
cout << "Test: " << test << endl;
seems a lot more intuitive to me than
printf("Test: %d\n", test);
If you'd never seem a printf before, there's no way you're going to know what that does.
In either case,
print "Test: " + test
(in several languages, including Python :( ) makes a lot more sense :)
I still use printf, mainly because it's easy to control the output format.
To me, type safety is not a main benefit here because it can be easily caught by test, given the fact that a console based program is far more easily to test with than UI based or web based applications. If you don't do test, more serious bugs can get over the compile time check any way.
I also don't agree with another reason that claims stream is more flexible due to interchangeability. It's equivalent to recommend using fprintf(fout,...) for interchangeability. If you need to redirect the output, use pipe. If you are in a library, why don't you just return a string and let the caller decide where it goes?
stringstream
is safer than snprintf
/sscanf
because it entirely avoids the possibility of buffer overflow (even "graceful failures").
Streams work with templates, whereas printf/scanf
don't.
精彩评论