Free Hosting

Overloading the I/O operators


Overloading <<
For classes that have multiple member variables, printing each of the individual variables on the screen can get tiresome fast. For example, consider the following class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Point
{
private:
    double X;
double Y;
 
public:
    Point(double dX=0.0, double dY=0.0)
    {
    X = dX;
    Y = dY;

    }
 
    double GetX() { return m_dX; }
    double GetY() { return m_dY; }
};
If you wanted to print an instance of this class to the screen, you’d have to do something like this:
1
2
3
4
Point cPoint(5.0, 6.0);
cout << "(" << cPoint.GetX() << ", " <<
    cPoint.GetY() << ", " <<
    cPoint.GetZ() << ")";
And that’s just for one instance! It would be much easier if you could simply type:
1
2
Point Point1(5.0, 6.0, 7.0);
cout << Point1;
and get the same result. By overloading the << operator, you can! Overloading operator<< is similar to overloading operator+ (they are both binary operators), except that the parameter types are different.
Consider the expression cout << cPoint. If the operator is <<, what are the operands? The left operand is the cout object, and the right operand is your Point class object. cout is actually an object of type ostream. Therefore, our overloaded function will look like this:
1
friend ostream& operator<< (ostream &out, Point &Pointtemp);
Implementation of operator<< is fairly straightforward -- because C++ already knows how to output doubles using operator<<, and our members are all doubles, we can simply use operator<< to output the member variables of our Point. Here is the above Point class with the overloaded operator<<.
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
class Point
{
private:
    double X;
double Y;
 
public:
    Point(double dX=0.0, double dY=0.0)
    {
    X = dX;
    Y = dY;

    }
 
    friend ostream& operator<< (ostream &out, Point &Pointtemp);
 
    double GetX() { return X; }
    double GetY() { return Y; }
};
 
ostream& operator<< (ostream &out, Point &Pointtemp)
{
    // Since operator<< is a friend of the Point class, we can access
    // Point's members directly.
    out << "(" << Pointtemp.X << ", " <<
        Pointtemp.Y<< ")";
    return out;
}
This is pretty straightforward -- note how similar our output line is to the line we wrote when we were outputting our members manually. They are almost identical, except cout has become parameter out!
The only tricky part here is the return type. Why are we returning an object of type ostream? The answer is that we do this so we can "chain" output commands together, such as cout << cPoint << endl;
Consider what would happen if our operator<< returned void. When the compiler evaluates cout << cPoint << endl;, due to the precedence/associativity rules, it evaluates this expression as (cout << cPoint) << endl;cout << cPoint calls our void-returning overloaded operator<< function, which returns void. Then the partially evaluated expression becomes: void << endl;, which makes no sense!
By returning the out parameter as the return type instead, (cout << cPoint) returns cout. Then our partially evaluated expression becomes: cout << endl;, which then gets evaluated itself!
Any time we want our overloaded binary operators to be chainable in such a manner, the left operand should be returned.
Just to prove it works, consider the following example, which uses the Point class with the overloaded operator<< we wrote above:
1
2
3
4
5
6
7
8
9
10
int main()
{
    Point cPoint1(2.0, 3.0);
    Point cPoint2(6.0, 7.0);
 
    using namespace std;
    cout << cPoint1 << " " << cPoint2 << endl;
 
    return 0;
}
This produces the following result:
(2.0, 3.0) (6.0, 7.0)
Overloading >>
It is also possible to overload the input operator. This is done in a manner very analogous to overloading the output operator. The key thing you need to know is that cin is an object of type istream. Here's our Point class with an overloaded operator>>:
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
class Point
{
private:
    double m_dX, m_dY, m_dZ;
 
public:
    Point(double dX=0.0, double dY=0.0, double dZ=0.0)
    {
    m_dX = dX;
    m_dY = dY;
    m_dZ = dZ;
    }
 
    friend ostream& operator<< (ostream &out, Point &cPoint);
    friend istream& operator>> (istream &in, Point &cPoint);
 
    double GetX() { return m_dX; }
    double GetY() { return m_dY; }
    double GetZ() { return m_dZ; }
};
 
ostream& operator<< (ostream &out, Point &cPoint)
{
    // Since operator<< is a friend of the Point class, we can access
    // Point's members directly.
    out << "(" << cPoint.m_dX << ", " <<
        cPoint.m_dY << ", " <<
        cPoint.m_dZ << ")";
    return out;
}
 
istream& operator>> (istream &in, Point &cPoint)
{
    in >> cPoint.m_dX;
    in >> cPoint.m_dY;
    in >> cPoint.m_dZ;
    return in;
}
Here's a sample program using both the overloaded operator<< and operator>>:
1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
    using namespace std;
    cout << "Enter a point: " << endl;
 
    Point cPoint;
    cin >> cPoint;
 
    cout << "You entered: " << cPoint << endl;
 
    return 0;
}
Assuming the user enters 3.0 4.5 7.26 as input, the program produces the following result:
You entered: (3, 4.5, 7.26)
Conclusion
Overloading operator<< and operator>> make it extremely easy to output your class to screen and accept user input.
Before we finish this lesson, there is one additional point that is important to make. The overloaded output operator<<
1
friend ostream& operator<< (ostream &out, Point &Pointtemp);
is actually better written as
1
friend ostream& operator<< (ostream &out, const Point &Pointtemp);
This way, you will be able to output both const and non-const objects.
However, for the overloaded input operator>>, you will have to leave Pointtemp as non-const because the overloaded operator>> modifies CPoint members.
By now, you should be starting to become comfortable with classes and passing parameters by reference. In future lessons, we will start making more of our parameters const references (which we should have been doing all along, but have abstained for purposes of simplicity). It is a good habit to get into early.

0 comments:

Blogger Template by Clairvo