14.4 — Overloading the I/O operators – Learn C++ (2023)

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:

class Point{private: double m_x{}; double m_y{}; double m_z{};public: Point(double x=0.0, double y=0.0, double z=0.0) : m_x{x}, m_y{y}, m_z{z} { } double getX() const { return m_x; } double getY() const { return m_y; } double getZ() const { return m_z; }};

If you wanted to print an instance of this class to the screen, you’d have to do something like this:

Point point{5.0, 6.0, 7.0};std::cout << "Point(" << point.getX() << ", " << point.getY() << ", " << point.getZ() << ')';

Of course, it makes more sense to do this as a reusable function. And in previous examples, you’ve seen us create print() functions that work like this:

class Point{private: double m_x{}; double m_y{}; double m_z{};public: Point(double x=0.0, double y=0.0, double z=0.0) : m_x{x}, m_y{y}, m_z{z} { } double getX() const { return m_x; } double getY() const { return m_y; } double getZ() const { return m_z; } void print() const { std::cout << "Point(" << m_x << ", " << m_y << ", " << m_z << ')'; }};

While this is much better, it still has some downsides. Because print() returns void, it can’t be called in the middle of an output statement. Instead, you have to do this:

int main(){ const Point point{5.0, 6.0, 7.0}; std::cout << "My point is: "; point.print(); std::cout << " in Cartesian space.\n";}

It would be much easier if you could simply type:

Point point{5.0, 6.0, 7.0};cout << "My point is: " << point << " in Cartesian space.\n";

and get the same result. There would be no breaking up output across multiple statements, and no having to remember what you named the print function.

Fortunately, by overloading the << operator, you can!

Overloading operator<<

Overloading operator<< is similar to overloading operator+ (they are both binary operators), except that the parameter types are different.

Consider the expression std::cout << point. If the operator is <<, what are the operands? The left operand is the std::cout object, and the right operand is your Point class object. std::cout is actually an object of type std::ostream. Therefore, our overloaded function will look like this:

// std::ostream is the type for object std::coutfriend std::ostream& operator<< (std::ostream& out, const Point& point);

Implementation of operator<< for our Point class 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<<.

#include <iostream>class Point{private: double m_x{}; double m_y{}; double m_z{};public: Point(double x=0.0, double y=0.0, double z=0.0) : m_x{x}, m_y{y}, m_z{z} { } friend std::ostream& operator<< (std::ostream& out, const Point& point);};std::ostream& operator<< (std::ostream& out, const Point& point){ // Since operator<< is a friend of the Point class, we can access Point's members directly. out << "Point(" << point.m_x << ", " << point.m_y << ", " << point.m_z << ')'; // actual output done here return out; // return std::ostream so we can chain calls to operator<<}int main(){ const Point point1{2.0, 3.0, 4.0}; std::cout << point1 << '\n'; return 0;}

This is pretty straightforward -- note how similar our output line is to the line in the print() function we wrote previously. The most notable difference is that std::cout has become parameter out (which will be a reference to std::cout when the function is called).

The trickiest part here is the return type. With the arithmetic operators, we calculated and returned a single answer by value (because we were creating and returning a new result). However, if you try to return std::ostream by value, you’ll get a compiler error. This happens because std::ostream specifically disallows being copied.

In this case, we return the left hand parameter as a reference. This not only prevents a copy of std::ostream from being made, it also allows us to “chain” output commands together, such as std::cout << point << std::endl;

Consider what would happen if our operator<< returned void instead. When the compiler evaluates std::cout << point << '\n', due to the precedence/associativity rules, it evaluates this expression as (std::cout << point) << '\n';. std::cout << point would call our void-returning overloaded operator<< function, which returns void. Then the partially evaluated expression becomes: void << '\n';, which makes no sense!

By returning the out parameter as the return type instead, (std::cout<< point) returns std::cout. Then our partially evaluated expression becomes: std::cout << '\n';, 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 (by reference). Returning the left-hand parameter by reference is okay in this case -- since the left-hand parameter was passed in by the calling function, it must still exist when the called function returns. Therefore, we don’t have to worry about referencing something that will go out of scope and get destroyed when the operator returns.

Just to prove it works, consider the following example, which uses the Point class with the overloaded operator<< we wrote above:

#include <iostream>class Point{private: double m_x{}; double m_y{}; double m_z{};public: Point(double x=0.0, double y=0.0, double z=0.0) : m_x{x}, m_y{y}, m_z{z} { } friend std::ostream& operator<< (std::ostream& out, const Point& point);};std::ostream& operator<< (std::ostream& out, const Point& point){ // Since operator<< is a friend of the Point class, we can access Point's members directly. out << "Point(" << point.m_x << ", " << point.m_y << ", " << point.m_z << ')'; return out;}int main(){ Point point1{2.0, 3.5, 4.0}; Point point2{6.0, 7.5, 8.0}; std::cout << point1 << ' ' << point2 << '\n'; return 0;}

This produces the following result:

Point(2, 3.5, 4) Point(6, 7.5, 8)

Overloading operator>>

It is also possible to overload the input operator. This is done in a manner analogous to overloading the output operator. The key thing you need to know is that std::cin is an object of type std::istream. Here’s our Point class with an overloaded operator>>:

#include <iostream>class Point{private: double m_x{}; double m_y{}; double m_z{};public: Point(double x=0.0, double y=0.0, double z=0.0) : m_x{x}, m_y{y}, m_z{z} { } friend std::ostream& operator<< (std::ostream& out, const Point& point); friend std::istream& operator>> (std::istream& in, Point& point);};std::ostream& operator<< (std::ostream& out, const Point& point){ // Since operator<< is a friend of the Point class, we can access Point's members directly. out << "Point(" << point.m_x << ", " << point.m_y << ", " << point.m_z << ')'; return out;}std::istream& operator>> (std::istream& in, Point& point){ // Since operator>> is a friend of the Point class, we can access Point's members directly. // note that parameter point must be non-const so we can modify the class members with the input values in >> point.m_x; in >> point.m_y; in >> point.m_z; return in;}

Here’s a sample program using both the overloaded operator<< and operator>>:

int main(){ std::cout << "Enter a point: "; Point point; std::cin >> point; std::cout << "You entered: " << point << '\n'; return 0;}

Assuming the user enters 3.0 4.5 7.26 as input, the program produces the following result:

You entered: Point(3, 4.5, 7.26)

Conclusion

Overloading operator<< and operator>> make it extremely easy to output your class to screen and accept user input from the console.

Quiz time

Take the Fraction class we wrote in the previous quiz (listed below) and add an overloaded operator<< and operator>> to it.

The following program should compile:

int main(){Fraction f1;std::cout << "Enter fraction 1: ";std::cin >> f1;Fraction f2;std::cout << "Enter fraction 2: ";std::cin >> f2;std::cout << f1 << " * " << f2 << " is " << f1 * f2 << '\n'; // note: The result of f1 * f2 is an r-valuereturn 0;}

And produce the result:

Enter fraction 1: 2/3Enter fraction 2: 3/82/3 * 3/8 is 1/4

Here’s the Fraction class:

#include <iostream>#include <numeric> // for std::gcd class Fraction{private:int m_numerator{};int m_denominator{}; public:Fraction(int numerator=0, int denominator=1):m_numerator{numerator}, m_denominator{denominator}{// We put reduce() in the constructor to ensure any new fractions we make get reduced!// Any fractions that are overwritten will need to be re-reducedreduce();}void reduce(){int gcd{ std::gcd(m_numerator, m_denominator) };if (gcd){m_numerator /= gcd;m_denominator /= gcd;}} friend Fraction operator*(const Fraction& f1, const Fraction& f2);friend Fraction operator*(const Fraction& f1, int value);friend Fraction operator*(int value, const Fraction& f1); void print() const{std::cout << m_numerator << '/' << m_denominator << '\n';}}; Fraction operator*(const Fraction& f1, const Fraction& f2){return Fraction(f1.m_numerator * f2.m_numerator, f1.m_denominator * f2.m_denominator);} Fraction operator*(const Fraction& f1, int value){return Fraction(f1.m_numerator * value, f1.m_denominator);} Fraction operator*(int value, const Fraction& f1){return Fraction(f1.m_numerator * value, f1.m_denominator);}

If you’re on a pre-C++17 compiler, you can replace std::gcd with this function:

#include <cmath> int gcd(int a, int b) { return (b == 0) ? std::abs(a) : gcd(b, a % b);}

Show Solution

Next lesson14.5Overloading operators using member functionsBack to table of contentsPrevious lesson14.3Overloading operators using normal functions

FAQs

How to overload operator ++ C++? ›

The postfix increment operator ++ can be overloaded for a class type by declaring a nonmember function operator operator++() with two arguments, the first having class type and the second having type int . Alternatively, you can declare a member function operator operator++() with one argument having type int .

Can we overload << operator in C++? ›

You can redefine or overload the function of most built-in operators in C++. These operators can be overloaded globally or on a class-by-class basis. Overloaded operators are implemented as functions and can be member functions or global functions.

What is operator overloading in C++ with example? ›

Operator overloading is used to overload or redefines most of the operators available in C++. It is used to perform the operation on the user-defined data type. For example, C++ provides the ability to add the variables of the user-defined data type that is applied to the built-in data types.

What are the 4 rules of operator overloading in C++? ›

Rules for operator overloading in C++
  • Only built-in operators can be overloaded. ...
  • The arity of the operators cannot be changed.
  • The precedence of the operators remains same.
  • The overloaded operator cannot hold the default parameters except function call operator “()”.
  • We cannot overload operators for built-in data types.
Jul 30, 2019

What is operator overloading in C++ +=? ›

Example: Overloading +=

This definition of operator+= adds the right-hand operand (the integer value) to the end of the list represented by the left-hand operand (the IntList whose member function is called). It would also be reasonable to define operator+= to add the integer to the front of the list.

Can we overload () operator? ›

The function call operator () can be overloaded for objects of class type. When you overload ( ), you are not creating a new way to call a function. Rather, you are creating an operator function that can be passed an arbitrary number of parameters.

Which C++ Cannot be overloaded? ›

Some operators cannot be overloaded which include sizeof operator, typeid, Scope resolution (::), Class member access operator (.), Ternary or conditional operator (?:). These operators cannot be overloaded because it can cause serious programming issues or errors and also it is syntactically not possible.

Which overloaded functions is not allowed in C++? ›

CPP. 2) Member function declarations with the same name and the name parameter-type-list cannot be overloaded if any of them is a static member function declaration.

What is function overloading syntax in C++? ›

Function overloading in C++ is a concept that allows two or more functions to have different logic but have a common function name. To overload functions, they must have a different= set of parameters, i.e., either have parameters of different data types or a different number of parameters in the function definition.

What are the 4 kinds of C++ operators? ›

Operators in C++ can be classified into 6 types:
  • Arithmetic Operators.
  • Assignment Operators.
  • Relational Operators.
  • Logical Operators.
  • Bitwise Operators.
  • Other Operators.

Why do we need operator overloading in C++? ›

Operator overloading in c++ enables programmers to use notation closer to the target domain. They provide similar support to built-in types of user-defined types. Operator overloading in c++ makes the program easier to understand.

What are types of overloading in C++? ›

There are mainly two types of overloading, i.e. function overloading and operator overloading.

What is overloading in OOP with example? ›

Overloading. Method overloading is a form of polymorphism in OOP. Polymorphism allows objects or methods to act in different ways, according to the means in which they are used. One such manner in which the methods behave according to their argument types and number of arguments is method overloading.

What are the five C++ operators? ›

C++ divides the operators into the following groups:
  • Arithmetic operators.
  • Assignment operators.
  • Comparison operators.
  • Logical operators.
  • Bitwise operators.

What are the 4 types of operators? ›

Operators
  • arithmetic operators.
  • relational operators.
  • logical operators.

How operator overloading is done? ›

Operator overloading is a technique by which operators used in a programming language are implemented in user-defined types with customized logic that is based on the types of arguments passed.

What is the rule for overload? ›

1) Only built-in operators can be overloaded. New operators can not be created. 2) Arity of the operators cannot be changed. 3) Precedence and associativity of the operators cannot be changed.

Is operator overloading bad? ›

Operator overloading is one of the most overused and misused capabilities of C++. It is your responsibility to make sure that you do not misuse an operator symbol so that another programmer's intuitive understanding of what the operator means is violated.

What is the correct way to overload +operator? ›

In case of operator overloading all parameters must be of the different type than the class or struct that declares the operator. Method overloading is used to create several methods with the same name that performs similar tasks on similar data types.

How do you overload a cout operator? ›

To get cout to accept a Date object after the insertion operator, overload the insertion operator to recognize an ostream object on the left and a Date on the right. The overloaded << operator function must then be declared as a friend of class Date so it can access the private data within a Date object.

Can you overload variables in C++? ›

You can redefine or overload most of the built-in operators available in C++. Thus, a programmer can use operators with user-defined types as well. Overloaded operators are functions with special names: the keyword "operator" followed by the symbol for the operator being defined.

What are the different types of overloading in C++? ›

There are mainly two types of overloading, i.e. function overloading and operator overloading.

How many operators we Cannot overload in C++? ›

The class member selector operator or Dot(.) operator cannot be overloaded and it will throw an error as seen in the output. Overloading the Dot(.) operator will arise a conflict in the meaning as the operator may be used for object overloading or to refer to an object of the class.

Can all C++ operators be overloaded? ›

Not all C++ operators can be overloaded. For an operator to be overloaded, at least one of the operands must be a user-defined object. Only existing operators can be overloaded. You cannot overload new operators.

Can == be overloaded? ›

The true and false operators must be overloaded together. Must be overloaded in pairs as follows: == and !=

References

Top Articles
Latest Posts
Article information

Author: Edwin Metz

Last Updated: 07/10/2023

Views: 5884

Rating: 4.8 / 5 (58 voted)

Reviews: 81% of readers found this page helpful

Author information

Name: Edwin Metz

Birthday: 1997-04-16

Address: 51593 Leanne Light, Kuphalmouth, DE 50012-5183

Phone: +639107620957

Job: Corporate Banking Technician

Hobby: Reading, scrapbook, role-playing games, Fishing, Fishing, Scuba diving, Beekeeping

Introduction: My name is Edwin Metz, I am a fair, energetic, helpful, brave, outstanding, nice, helpful person who loves writing and wants to share my knowledge and understanding with you.