Chapter 19. Friend Classes and Friend Functions

FAQ 19.01 What is a friend?

image

A friend is an entity to which a class grants access authority.

Friends can be functions, other classes, or individual member functions of other classes. Friend classes are used when two or more classes are designed to work together and need access to each other's implementation in ways that the rest of the world shouldn't be allowed to have. In other words, they help keep private things private. For instance, it may be desirable for class DatabaseCursor to have more privilege to the internals of class Database than main() has.

In the early days of OO, people had all sorts of strange ideas. Some people thought that the class was the fundamental unit that had to be encapsulated and concluded that friendship was evil. Experience has shown that some abstractions require more than one class, and that the abstraction needs the encapsulation barriers more than the component classes. As long as they are used properly, friends enhance the encapsulation of abstractions.

Friend classes normally imply that one abstraction (a database with multiple cursors, for example) is implemented using several distinct classes (Database and DatabaseCursor). Using several classes to implement one abstraction is especially useful when the various classes have different lifetimes or different cardinalities. For example, it is likely that there are an arbitrary number of DatabaseCursor objects for any given Database object.

image

It would be a bad idea to force class Database and class DatabaseCursor into one class by moving the member functions of DatabaseCursor into class Database (so we would now have Database::getCurrentRecord()). This would impose a one-cursor-per-database policy and would cause Database to manage both the data and a current position within that data.

The UML diagram for this friendship relationship follows.

image

FAQ 19.02 What's a good mental model for friend classes?

A secret handshake or other technique to exchange information with a confidant in such a way that normal people are unable to access the same secrets. Friend classes prevent users (the normal people) from seeing the information being exchanged through the secret codes that connect the various objects (confidants).

The overall effect is to keep private things private.

The alternative to granting special access privileges between the classes would be for the classes to declare public: member functions that allow anyone to manipulate the class's private: members. For instance, in the DatabaseCursor example, class Database would need to provide public: member functions to manipulate its cache, B-tree, file system, and so on. Although the implementation bits would be encapsulated, the implementation technique would be exposed. Subsequent changes to the implementation technique would break users' code.

In the traditional software realm, friendship is called tight cohesion, and is, within limits, considered good.

FAQ 19.03 What are some advantages of using friend classes?

Friend classes are useful when a class wants to hide features from users that are needed only by another, tightly coupled class. Compared to making a member public:, it is sometimes better to make the member private:, which eliminates potential misuse by unknown users, and grant friendship status to the tightly cohesive class, thereby keeping implementation details hidden from the rest of the world.

Friend classes also arise when a member function on a class needs to maintain state between calls and when multiple copies of this state must exist. Under these circumstances, the member function becomes a friend class, and the multiple copies of the state become multiple objects of the friend class.

FAQ 19.04 Do friends violate the encapsulation barrier?

Not necessarily.

If the encapsulation barrier is narrowly defined as the suite of member functions on a class, then friends violate the encapsulation barrier. However, this is a naive view of encapsulation, and applying it consistently actually degrades the overall encapsulation of a system. For example, if another entity needs to be part of the same abstraction, this naive approach suggests that the first class should expose its implementation technique via an unnecessarily large suite of get/set member functions.

The enlightened view is that the encapsulation barrier encapsulates an abstraction, not just a class. For example, the earlier example of a database with multiple cursors illustrates an abstraction that is too rich to be implemented by a single class. In cases like this, friend classes are a valuable way of hiding the (possibly complex) interrelationships between the various pieces of the abstraction.

Friends don't violate the encapsulation barrier; they are part of the encapsulation barrier.

FAQ 19.05 What is a friend function?

A friend function is a nonmember function that has been granted access to a class's non-public: members. This improves an interface without breaking encapsulation.

For example, the syntax most objects use for printing is cout << x, where x is the object being printed and cout is the output stream (ostream) on which the object is being printed. This printing service is provided by operator<<, which needs to be a friend function of the class of x rather than a member function of the class of x, because the ostream needs to be on the left side of the << operator and the object being printed on the right side. In general, binary operators can be member functions only if the member function is attached to the left hand argument of the operator.

image

FAQ 19.06 When should a function be implemented as a friend function rather than a member function?

Whenever it improves readability of user code.

Generally, member functions are used rather than friend functions. However if a friend function would make the code that uses the class more readable, a friend function should be used.

It is important that the decision be based not on the readability of the code within the class but rather on the readability of the code that uses the class. There isn't that much difference between the internal implementation details within a friend function and those within a member function. But even more important is the economics of the situation. It's more important to focus on the many users of the class than it is to worry about the class itself.

The point of friend functions is that they allow the syntax for the user code to be intuitive while still maintaining the class's encapsulation barrier. This can lead to easier-to-use classes, which reduces education costs and improves the quality of the user code—intuitive interfaces are abused less often.

FAQ 19.07 What are some guidelines to make sure friend functions are used properly?

Friend functions should be part of the class's public: interface, and their code should be owned by the class's owner.

Guideline 1: Friend functions should make the user's code easier to understand. Look at some sample syntax of how a user would use the class with the friend function, and compare it with the moral equivalent of the sample syntax if the friend function were changed into a member function. The friend function version should be used if and only if it results in more intuitive user code (see FAQ 19.06).

Guideline 2: Friend functions should be used only for operations that are part of the public: interface of a class. They should not be used every time someone wants to do something tricky with the class. If a user has a function that needs to access the innards of your class (for example, because the class's current public: interface isn't powerful enough), fix the problem (the interface) rather than patching the symptoms. Don't grant friendship to everyone.

Guideline 3: A friend function or class should be under the political and technical authority of the same team that owns the class itself. Granting friendship status to a function or class under the political authority of a team other than the one implementing the class results in a scheduling headache—changes that involve coordinating multiple participants who may not always be in a position to handle the requested modifications in a timely manner is a nightmare.

FAQ 19.08 What does it mean that friendship isn't transitive?

A friend of a friend isn't (necessarily) a friend.

Friendship is personal; it is explicitly granted to a particular, named individual. All friends of a class are declared explicitly in the body of the class. This clearly identifies the entities that need to be updated when the private: part of a class is changed.

In the following code, operator<< is a friend of BinaryTree, which is a friend of BinaryTreeNode, but this does not make operator<< a friend of BinaryTreeNode.

image

If operator<< needs to access BinaryTreeNode::left_ or BinaryTreeNode::right_, it must be made a friend of BinaryTreeNode as well:

image

Note that the compiler doesn't care where a friend declaration appears within a class, so the placement is normally done to make the code easily readable by other programmers (see FAQ 19.12). In the example, normal users might be somewhat confused by the friendship relationship between BinaryTreeNode and operator<<, so it has been moved out of the public: section (the public: section is where normal users look to find out how to use a class).

FAQ 19.09 What does it mean that friendship isn't inherited?

Just because someone trusts you does not automatically mean they will trust your children.

Suppose class Fred grants friendship privileges to another class Base and class Derived is derived from class Base. Derived does not automatically have friendship privileges to access the innards of Fred just because its base class is a friend of Fred. This rule improves encapsulation. Without this rule, anyone could automatically gain friendship (and access to internals) by deriving from a known friend.

image

In the following example, an EggCarton is not supposed to have more than a dozen eggs (numEggs_ <= 12). Class EggCartonFiller is trusted not to violate the semantics of an EggCarton, so EggCarton makes EggCartonFiller a friend. This friendship allows EggCartonFiller::addAnEgg() to access EggCarton::numEggs_.

image

If friendship were inherited, anyone could create a class derived from EggCartonFiller and possibly violate the semantics of an EggCarton.

image

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.

image

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.

image

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).

image

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<<.

FAQ 19.11 What qualities suggest a friend function rather than a member function?

The three P's of friendship: position, promotion, or perception.

Position:Use a friend function when the object being operated on can't appear as the leftmost argument. For example, the syntax to print an object n is usually cout << n, where cout can be replaced by any ostream. Notice that n is not the leftmost argument and therefore operator<< cannot be a member of n's class. If operator<< needs access to n's internal state, it must be a friend of n's class.

image

Promotion: Use a friend function to allow promotion of the leftmost argument. For example, the Fraction class might want to support 5*n, where n is a Fraction object. This may require promoting the leftmost argument from an int to a Fraction, where this is implemented by passing a single int parameter to Fraction's constructor—Fraction(5). The operator* needs to be a friend because C++ never automatically promotes the this object in a member function invocation.

image

Perception: Use a friend function when it leads to a user syntax that is more intuitive. For example, two possible syntaxes for computing the square of a fraction n are n.square() and square(n) (for example, 1/2 squared is 1/4). If the operation is constructive (if n is unchanged), square(n) may be preferred because n.square() might be incorrectly perceived as squaring n itself.

image

Of the three P's for choosing between friend functions and member functions, perception is the most subjective. In many cases involving perception, a static member function such as Fraction::square(n) is better than a friend function.

FAQ 19.12 Should friend functions be declared in the private:, protected:, or public: section of a class?

For documentation purposes, they should be declared in the public: section of a class. The compiler ignores the access level (private:, protected:, or public:) where friend functions are declared. However, for documentation purposes, they should normally be declared in the public: part of the class since friend functions are inherently public: (most friend functions are non-member functions and are therefore conceptually declared outside the class).

For an exception to this guideline, see FAQ 19.08.

FAQ 19.13 What is a private class?

A private class is a class created only for implementation purposes and is hidden from normal users. Typically, all its constructors (and often all its other members as well) are private: and it declares another class as its friend. Because the private class lacks public: constructors or member functions, only the designated friends and other instances of the private class can create or use instances of the private class.

For example, the Node class associated with a linked list class might be so specialized that no other class would benefit from reusing it. In this case the Node class can be a private class and can declare the linked list class as a friend. In the following code, class Node is nested inside class List. Although not strictly part of the private class concept, this technique has the further benefit of reducing the number of names in the outer namespace (nesting Node inside List removes the name Node from the namespace that List is in).

image

image

FAQ 19.14 How are objects of a class printed?

Objects of a class are normally printed via a friend function called operator<<. Here is an example of such a friend function.

image

The function operator<< is a friend rather than a member, so the Fred parameter appears on the right side of the <<.

FAQ 19.15 How do objects of a class receive stream input?

Objects of a class normally receive stream input via a friend function called operator>>. Here is an example of such a friend function.

image

The Fred argument of operator>> is passed by reference (as opposed to const reference). This allows operator>> to change the caller's Fred, which is, of course, the whole point of stream input.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset