Defining stream operators

Location
  1. Courses

    /

  2. Complete C++ Course

    /

  3. Object oriented programming

    /

  4. Defining stream operators

Stream operators

The stream operator overloads with stream objects like std::cin and std::cout as left operand and variables of primitive types as right operand are already defined. We can then directly output/input variables of primitive types to/from stream objects:

float var; std::cin >> var; // Reads a floating-point number from the input stream 'std::cin'. std::cout << var; // Writes a floating-point number to the output stream 'std::cout'.

What if we wanted to do the same, but with objects of our classes as right operands? All we would have to do is define operator overloads!

Note that the input/output stream operators are also called shift operators when used to shift the bits of variables (We saw that in the page about bitwise operators).

std::ostream and std::istream

The stream classes of C++ are std::ostream and std::istream. They can be seen as the C++ equivalent of the structure FILE. However, unlike the structure FILE which can represent both an input and output stream, std::ostream is an output only stream and std::istream is an input stream. The objects std::cout, std::cerr and std::cin are the C++ equivalent of stdout, stderr and stdin. The streams std::cout and std::cerr are objects of type std::ostream and std::cin is an object of type std::istream.

Output stream operator (Left shift)

Let us define the following class:

Name.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef NAME_HPP #define NAME_HPP #include <string> class Name { public: Name(); std::string const& getFirstName() const; std::string const& getLastName() const; void setFirstName(const std::string & firstName); void setLastName(const std::string & lastName); protected: std::string _firstName; std::string _lastName; }; #endif

Name.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "Name.hpp" #include <iostream> Name::Name() : _firstName("Kelly"), _lastName("Parker") { } std::string const& Name::getFirstName() const { return _firstName; } std::string const& Name::getLastName() const { return _lastName; } void Name::setFirstName(const std::string & firstName) { _firstName = firstName; } void Name::setLastName(const std::string & lastName) { _lastName = lastName; }

If we wanted to print the members of an object of the class Name, we could simply print its members one by one, like the following:

Name name; std::cout << name.getFirstName() << " " << name.getLastName() << std::endl;

What if we want to simply write the following?

Name name; std::cout << name << std::endl;

The operator << with an object of type std::ostream as the left operand is already defined for some types (primitives (int, double...), std::string, char pointers (null-terminated strings)...) as the right operand. However, for our classes as right operand, we must define the operator ourselves.

To allow our classes to be directly outputted, using the << operator, to streams like std::cout, we must define the operator overload as a function, because the left operand must be of std::ostream type.

Let us define the stream operator to output an object of type Name to a stream of type std::ostream:

1
2
3
4
5
6
7
8
9
void operator<<(std::ostream & out, const Name & name); void operator<<(std::ostream & out, const Name & name) { /* Writes the members of 'name' to the stream. */ out << name.getFirstName() << " " << name.getLastName(); }

Now we could do the following:

Name name; std::cout << name;

However, the following would cause an error:

Name name; std::cout << name << std::endl;

Why? The reason is that the stream operator we defined returns nothing, so when the first stream operation is finished (std::cout << name), the second one (<< std::endl;) receives nothing (void) instead of an output stream as left operand. This is why it is recommended to make the stream operators return a reference to the stream object. When a stream operator returns back a reference to the stream and there is other stream operations following it, the next stream operation takes the returned reference to the stream as its left operand.

Declaration

1
std::ostream & operator<<(std::ostream & out, const Name & name);

Definition

1
2
3
4
5
6
7
8
9
10
11
12
std::ostream & operator<<(std::ostream & out, const Name & name) { /* Writes the members of 'name' to the stream. */ out << name.getFirstName() << " " << name.getLastName(); /* Returns a reference to the stream so the following output stream operations can use it. */ return out; }

Now it works:

1
2
3
4
5
6
7
8
9
10
#include "Name.hpp" int main() { Name name; std::cout << name << std::endl; return 0; }

Input stream operator (Right shift)

Defining an input stream operator >> overload to read from an input stream object, like std::cin, is very similar to defining an output stream operator << overload to write to an output stream object, like std::cout. The only differences are that the left operand and the return type must be of type std::istream and the instructions inside the operator definition must input from the stream instead of output.

Here is an example of an input stream operator overload to read from an input stream object to an object of type Name:

Declaration

1
std::istream & operator>>(std::istream & in, Name & name);

Definition

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
std::istream & operator>>(std::istream & in, Name & name) { /* Inputs two strings from the stream 'in'. */ std::string firstName, lastName; in >> firstName >> lastName; /* Assigns the first string read to the member '_firstName' of the object 'name' and the second one to the member '_lastName'. */ name.setFirstName(firstName); name.setLastName(lastName); /* Returns a reference to the stream so the following input stream operations can use it. */ return in; }

Now we can directly input, using the input stream operator, from an object of type std::istream, to an object of type Name:

1
2
3
4
5
6
7
8
9
10
#include "Name.hpp" int main() { Name name; std::cin >> name; return 0; }

Whole example

Here is the whole example, with the operator overloads from above:

Name.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef NAME_HPP #define NAME_HPP #include <string> class Name { public: Name(); std::string const& getFirstName() const; std::string const& getLastName() const; void setFirstName(const std::string & firstName); void setLastName(const std::string & lastName); protected: std::string _firstName; std::string _lastName; }; std::ostream & operator<<(std::ostream & out, const Name & name); std::ostream & operator>>(std::istream & in, const Name & name); #endif

Name.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include "Name.hpp" #include <iostream> Name::Name() : _firstName("Kelly"), _lastName("Parker") { } std::string const& Name::getFirstName() const { return _firstName; } std::string const& Name::getLastName() const { return _lastName; } void Name::setFirstName(const std::string & firstName) { _firstName = firstName; } void Name::setLastName(const std::string & lastName) { _lastName = lastName; } std::ostream & operator<<(std::ostream & out, const Name & name) { /* Writes the members of 'name' to the stream. */ out << name.getFirstName() << " " << name.getLastName(); /* Returns a reference to the stream so the following output stream operations can use it. */ return out; } std::istream & operator>>(std::istream & in, Name & name) { /* Inputs two strings from the stream 'in'. */ std::string firstName, lastName; in >> firstName >> lastName; /* Assigns the first string read to the member '_firstName' of the object 'name' and the second one to the member '_lastName'. */ name.setFirstName(firstName); name.setLastName(lastName); /* Returns a reference to the stream so the following input stream operations can use it. */ return in; }

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "Name.hpp" int main() { Name name; /* Reads a first name and last name from the console. */ std::cin >> name; /* Outputs them to the console. */ std::cout << name << std::endl; return 0; }