This post is part of a series of tutorials on C++
One operator that can be insanely useful for debugging is to code stream operators, also know as
operator>>(). You’ll notice that
>> are the same symbols used for
cin respectively. Having these operators implemented can be useful to writing contents of a class to standard input and standard output. They can also be useful to parsing the contents of a file directly into instances of a class, as file streams use the same operators.
The basic form is quite easy to implement:
The above is a template for the stream insertion operator, whose purpose is to “insert”
obj into the stream
os. Note that this function returns the stream itself. This allows for chain, e.g.
As the expression
std::cout << a << b will evaluate as
(std::cout << a) << b, allowing one to first evaluate to
std::cout.operator<<(a), which then returns the updated
std::cout so that you can do
std::cout.operator(b) after inserting
a into the stream.
It’s handy to make this function a
friend so that it can access private members, but this is not necessary. Some people prefer to keep it an external method, and instead code a function that converts the class member to a
string. This is extremely similar to Java’s
toString() method, and looks like this:
There’s a lot going on in the above. First off, the easy part to understanding is how
operator<<() works now. First, we take our instance of
obj, and cast
obj to be of type
string. What is a bit more subtle is that the cast that we did uses an operator! In fact, in C++, you can code all sorts of casting operators to cast your complex types to a variety of other types, both complex (like
std::string) or primitives (like
float). Our casting operator could have just as easily been a member function
std::string toString(), but making it an operator allows us to use C++-style casting. Neat!
Note that these cast operators seem a bit weird. They do not have a return type stated, although they are meant to return whatever type you are casting to.
The above leaves a lot open-ended. Folks will often using the std::string() cast to format complex types in ways that are easy to quickly glance at and debug. There are lots of options here.
Stream extraction operators can be really useful for parsing data. Their basic form looks like this:
The above logic is a bit more complicated, since you have to be careful to check that a valid instance of
Foo is found. Typically, you might do this by reading the individual members of
Foo one at a time. For example, in our
ThreeLetterWord class, we might first read each character of the
ThreeLetterWord, and as long as we found three characters, then updated
obj. Otherwise, we set the
failbit and return.