C++ Tricks of the Trade: Friend Functions - Commutative Operators
(Page 3 of 5 )
Overloading operator+ allows the rational class to behave more like an intrinsic data type. To add two rational numbers, we can overload the operator as a class member:
const CRational CRational::operator+(const CRational &r)
{
// add *this to r, and return the result by value.
// ...
}The operator+ function can also be overloaded to handle the addition of a rational, and a non-rational type. If n is an integer, and r is a rational then we could implement r + n as a class member:
const CRational CRational::operator+(const int n)
{
// add *this to n, and return the result by value
// ...
}Since operator+ is an example of a commutative operator, we would expect r + n to have the same semantics as n + r. In the second case we need to use a non-member function:
class CRational
{
friend const CRational
operator+(const int n, const CRational &r);
// ...
}This function could be written as a non-friend function, provided that get/set member functions are used. Doing so would turn the function into a plain non-member function, and so it would not be clear how it interacts with the class. By using a friend function, readers of the class declaration see that operator+ forms part of the CRational interface, and is semantically part of the class.
Friends and Virtual FunctionsA problem with friends is that class member functions can be virtual while non-member functions can’t. The equivalent of a virtual friend function is useful when deriving from a class that has a friend function.
An example of where the equivalent of a virtual friend would be useful is if serializing an entire class hierarchy. The trick in doing this is to turn the friend function into a lightweight inline proxy function, which forwards the request back to the class. For example, consider the simple base class:
class CPerson
{
friend ostream &operator<< (ostream &out, CPerson &person);
// ...
protected:
virtual ostream &display(ostream &out);
// ...
};The friend function forwards the display request back to the CPerson class. Note that we pass an instance of CPerson by reference:
inline ostream &operator<< (ostream &out, CPerson &person)
{
return person.display(out);
}In a class hierarchy using the base CPerson class, each derived class has its own implementation of the display function:
class CBob : public CPerson
{
protected:
virtual ostream &display(ostream &out)
{
out << "Bob";
return out;
}
};This is just as efficient as using a virtual friend function (if such a thing existed) since a good compiler will replace a call to operator<< with a call to the display function, which each derived class implements.
Next: Friend Classes >>
More C++ Articles
More By Kais Dukes