C++ FAQ Celebrating Twenty-One Years of the C++ FAQ!!!
(Click here for a personal note from Marshall Cline.)
Section 15:
[15.9] But shouldn't I always use a printOn() method rather than a friend function?

No.

The usual reason people want to always use a printOn() method rather than a friend function is because they wrongly believe that friends violate encapsulation and/or that friends are evil. These beliefs are naive and wrong: when used properly, friends can actually enhance encapsulation.

This is not to say that the printOn() method approach is never useful. For example, it is useful when providing printing for an entire hierarchy of classes. But if you use a printOn() method, it should normally be protected, not public.

For completeness, here is "the printOn() method approach." The idea is to have a member function, often called printOn(), that does the actual printing, then have operator<< call that printOn() method. When it is done wrongly, the printOn() method is public so operator<< doesn't have to be a friend — it can be a simple top-level function that is neither a friend nor a member of the class. Here's some sample code:

#include <iostream>

class Fred {
public:
  void printOn(std::ostream& o) const;
  ...
};

// operator<< can be declared as a non-friend [NOT recommended!]
std::ostream& operator<< (std::ostream& o, Fred const& fred);

// The actual printing is done inside the printOn() method [NOT recommended!]
void Fred::printOn(std::ostream& o) const
{
  ...
}

// operator<< calls printOn() [NOT recommended!]
std::ostream& operator<< (std::ostream& o, Fred const& fred)
{
  fred.printOn(o);
  return o;
}
People wrongly assume that this reduces maintenance cost "since it avoids having a friend function." This is a wrong assumption because:
  1. The member-called-by-top-level-function approach has zero benefit in terms of maintenance cost. Let's say it takes N lines of code to do the actual printing. In the case of a friend function, those N lines of code will have direct access to the class's private/protected parts, which means whenever someone changes the class's private/protected parts, those N lines of code will need to be scanned and possibly modified, which increases the maintenance cost. However using the printOn() method doesn't change this at all: we still have N lines of code that have direct access to the class's private/protected parts. Thus moving the code from a friend function into a member function does not reduce the maintenance cost at all. Zero reduction. No benefit in maintenance cost. (If anything it's a bit worse with the printOn() method since you now have more lines of code to maintain since you have an extra function that you didn't have before.)
  2. The member-called-by-top-level-function approach makes the class harder to use, particularly by programmers who are not also class designers. The approach exposes a public method that programmers are not supposed to call. When a programmer reads the public methods of the class, they'll see two ways to do the same thing. The documentation would need to say something like, "This does exactly the same as that, but don't use this; instead use that." And the average programmer will say, "Huh? Why make the method public if I'm not supposed to use it?" In reality the only reason the printOn() method is public is to avoid granting friendship status to operator<<, and that is a notion that is somewhere between subtle and incomprehensible to a programmer who simply wants to use the class.

Net: the member-called-by-top-level-function approach has a cost but no benefit. Therefore it is, in general, a bad idea.

Note: if the printOn() method is protected or private, the second objection doesn't apply. There are cases when that approach is reasonable, such as when providing printing for an entire hierarchy of classes. Note also that when the printOn() method is non-public, operator<< needs to be a friend.