FAQ 19.10 What does it mean that friends aren't virtual?Friend functions don't bind dynamically. However there is a simple one-line idiom that enables the functionality of a virtual function (that is, dynamic binding) with the syntax of a friend function. This idiom is called the virtual friend function idiom. The virtual friend function idiom provides the effect of friend functions that bind dynamically; it is used when the syntax of a friend function is desired but the operation must be dynamically bound. Simply put, use a friend function that calls a protected: virtual member function. For example, suppose class Shape is an abstract base class (ABC), and a Shape is printed via cout << aShape, where aShape is a Shape&, which refers to an object of a derived class, such as Circle. To use the virtual friend function idiom, operator<< would be a friend of Shape and would call a protected: pure virtual member function such as print(ostream&) const. #include <iostream> using namespace std; class Shape { public: virtual ~Shape() throw(); friend ostream& operator<< (ostream& ostr, const Shape& s) throw(); protected: virtual void print(ostream& ostr) const throw() = 0; }; inline ostream& operator<< (ostream& ostr, const Shape& s) throw() { s.print(ostr); <-- 1 return ostr; } Shape::~Shape() throw() { }
Because print() is virtual, the right implementation will always be invoked. Because print() is pure virtual, concrete derived classes are required to provide a definition Shape doesn't have enough knowledge about itself to print itself. class Circle : public Shape { public: Circle() throw(); protected: virtual void print(ostream& ostr) const throw(); <-- 1 float radius_; }; Circle::Circle() throw() : radius_(42) { } void Circle::print(ostream& ostr) const throw() { ostr << "Circle of radius " << radius_; }
Because print() is protected:, users must use the official syntax provided by operator<< (this avoids cluttering the interface with two ways of doing the same thing). void userCode(Shape& s) { cout << s << '\n'; } <-- 1 int main() { Circle c; userCode(c); }
The output is Circle of radius 42 Note that there is only one operator<< for the entire Shape hierarchy. Derived classes provide a definition for print(ostream&) const, but they do not declare or define operator<<. |