Overloading operator<< for printing as a member

20,655

Solution 1

You have stumbled across the canonical way to implement this functionality. What you have is correct.

Solution 2

It needs to be a non-member, since the class forms the second argument of the operator, not the first. If the output can be done using only the public interface, then you're done. If it needs access to non-public members, then you'll have to declare it a friend; that's what friends are for.

class TestClass {
public:
    friend ostream& operator<<(ostream& os, TestClass const & tc) {
        return os << "I'm a friend of the class, msg=" << tc.msg << endl;
    }

private:
    string msg;
};

Solution 3

I believe one popular way to do this is a non-member, non-friend free operator<< that calls a public non-virtual print method within your class. This print method can either do the work or delegate to a protected virtual implementation.

class TestClass {
public:
    ostream& print(ostream& os) const {
        return os << "I'm in the class, msg=" << msg << endl;
    }

private:
    string msg;
};


ostream& operator<<(ostream& os, TestClass& obj) {
    return obj.print(os);
}

int main(int argc, char** argv) {
    TestClass obj;
    cout << obj;

    return 0;
}

Solution 4

You can make it member of the class, that is on the left of <<, which is ostream in your case.

What you can do, though, is have a base class with void do_stream(ostream& o); member for all your streamables and non-member operator<< that would call it.

Share:
20,655
ChewToy
Author by

ChewToy

Updated on December 13, 2020

Comments

  • ChewToy
    ChewToy over 3 years

    Is there a way to overload the << operator, as a class member, to print values as a text stream. Such as:

    class TestClass {
    public:
        ostream& operator<<(ostream& os) {
            return os << "I'm in the class, msg=" << msg << endl;
        }
    
    private:
        string msg;
    };
    
    
    int main(int argc, char** argv) {
        TestClass obj = TestClass();
        cout << obj;
    
        return 0;
    }
    

    The only way I could think of was to overload the operator outside of the class:

    ostream& operator<<(ostream& os, TestClass& obj) {
        return os << "I'm outside of the class and can't access msg" << endl;
    }
    

    But then the only way to access the private parts of the object would be to friend the operator function, and I'd rather avoid friends if possible and thus ask you for alternative solutions.

    Any comments or recommendations on how to proceed would be helpful :)

  • James Kanze
    James Kanze over 12 years
    There's usually no need to declare the function as a friend; anything you output is part of the public interface of the class. (It's often appropriate to declare it as a friend in a template, in order to use the Barton and Nackman trick, but that's a different issue.)
  • Sebastian Mach
    Sebastian Mach over 12 years
    This is seriously not the only way to implement it. I've seen and implemented most stream operators without binding them as friend. Only were necessary, friend should be used.
  • broc
    broc over 12 years
    I support this solution above using a friend declaration. It maintains encapsulation and gets the job done.
  • James Kanze
    James Kanze over 12 years
    @broc I don't see any real difference in the encapsulation. In one case, the author of the class explicitly gives access rights to a single instance of operator<<(); in this method, he explicitly gives access rights to a function named print(). Where is the difference? (There may be other reasons to use the print() method, but more or less encapsulation isn't one of them.)
  • James Kanze
    James Kanze over 12 years
    @phresnel I tend to use the print method a lot, with class templates. Not to gain access, but to be able to define the function within the class.
  • Luchian Grigore
    Luchian Grigore over 12 years
    In his case, the member is private, so friendship is required. It's much simpler than adding getters just to be used by the stream.
  • Michael Krelin - hacker
    Michael Krelin - hacker over 12 years
    I don't. I'm just stating the fact that if one wants it to be a class member, the class should be std::ostream. Which basically means no, you don't want it be a class member.
  • broc
    broc over 12 years
    @JamesKanze You could argue that declaring a member public is just the author giving access rights to everyone explicitly and hence it does not break encapsulation. Whether you buy this or not depends on where you draw the line. I personally think that the line must be drawn as far as possible in the opposite direction; having the print method allows you to distribute the class in a .dll, distribute it without source and still allows the user of the library to write their overloaded operator<< for their convenience. Can you get any more encapsulated? From the perspective...
  • broc
    broc over 12 years
    @JamesKanze of just the language I can't disagree with you, however, the language is used to create an executable or a library and I think that for the purpose of developing professionally, the concept of encapsulation must reach further than just thinking in terms of the language.
  • James Kanze
    James Kanze over 12 years
    @broc Declaring a member public loosens encapsulation, since it makes the member part of the public interface. Declaring a function friend makes that function part of the class' public interface; by making a function part of the interface, rather than data, you are actually increasing encapsulation. Too many functions (or poorly designed functions, like getters and setters) weaken encapsulation too, but in this regard, whether the function is a friend or a member is irrelevant.
  • Udayraj Deshmukh
    Udayraj Deshmukh almost 7 years
    Thanks, this solution also works for output of other overloaded operators. For eg cout<<(s1+s2) didn't work when using other methods. Any reason why this might be happening?
  • phinz
    phinz over 5 years
    This is not correct because it won't compile. In the setup from the question one would have to write something ugly like obj<<cout;.
  • dunadar
    dunadar almost 5 years
    For a more exhaustive answer around this topic, see accepted answer in stackoverflow.com/questions/13516720/where-to-put-overload-c‌​ode