Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my overloaded << operator not working?

Tags:

c++

I'm trying to understand how to properly overload the "<<" operator so that I can use

std::cout << my_object; 

to print useful debug messages. In particular, I need to have an implementation of << for each of my subclasses, so I'm declaring << to be virtual in the superclass.

Right now I'm stuck with the following piece of code

#include <iostream>

using namespace std;

class Shape {
public:
    virtual ~Shape() { };

    virtual ostream& operator<<(std::ostream &strm) = 0;
};

class Square : public Shape {
    int size;
public:
    Square() { size = 10; }
    ~Square() { }

    ostream& operator<<(std::ostream &strm) {
        return strm << "a square with size " << size;
    }
};

int main() {
    Square *my_square = new Square();

    cout << "my_square is " << my_square << "\n";
}

which (I think) should be working, but doesn't. What I get when using "<<" is that the pointer value of my_square gets printed, rather than the result of the overloaded << .

$ ./a.out 
my_square is 0xcacc20

What am I missing here?

like image 400
mbrandalero Avatar asked Jan 06 '23 20:01

mbrandalero


2 Answers

operator<< can't be a member function. This is because of the order of the arguments. The stream has to come first.

When calling an overloaded operator, such as:

os << object;

the compiler will attempt to look up both

os.operator<<(object);

and

operator<<(os, object);

(The rules for this can be rather complex, I won't attempt to describe them here.)

Because the stream always comes on the left, your member function will never be found, since it would have to be called as:

object.operator<<(os);

You need to write a free function like:

ostream& operator<<(std::ostream &strm, Square const& square) {
    return strm << "a square with size " << square.size();
}

(where Square::size() returns the size member).

Then you need to remember to dereference your pointer too:

std::cout << *my_square << '\n';

Although I see no reason to be dynamically allocating my_square in this example anyway. Just stick it on the stack as a local variable.


If the aim here is ultimately to be able to print any Shape&, and have the printed output follow the "real" type, you would need to create:

virtual std::ostream& print(std::ostream&) const = 0;

in the Shape base class, and override it in each derived class, then have a free function:

std::ostream& operator<<(std::ostream& os, Shape const& shape)
{
    return shape.print(os);
}

It is often advised to make all binary operators on your type non-member functions, so that both arguments are treated equally, and the operation remains commutative. See Scott Meyers, Effective C++ (3rd Edition), Item 24, (or find a summary online).

like image 126
BoBTFish Avatar answered Jan 09 '23 09:01

BoBTFish


As noted by others, the problem is that operator << can't be member function (because of the order of arguments). The canonical way to do this is to have operator <<(const Shape&) call a virtual function in Shape

class Shape {
    friend ostream& operator<<(std::ostream& str, const Shape& shape);
    virtual void do_print(ostream& str) = 0;
public:
    virtual ~Shape() { };
};

ostream& operator<<(std::ostream& str, const Shape& shape) {
    shape.do_print(str);
    return str;
}

Note that it is legal to have do_print be private, even though it is going to be (must be) overridden by derived classes. You could make it protected though if you like.

like image 39
Martin Bonner supports Monica Avatar answered Jan 09 '23 11:01

Martin Bonner supports Monica