Channels ▼
RSS

Friendship and the Attorney-Client Idiom


January, 2006: Friendship and the Attorney-Client Idiom

Alan R. Bolton is a Senior Development Engineer at Extended Systems in Boise, Idaho, specializing in data-synchronization software. He can be reached at alan.bolton@extendedsystems.com.


The Three Ps


There is a long-standing prejudice in the C++ community against declaring friend functions and classes. Since the earliest days of C++, people have claimed that friendship breaks encapsulation [1]; and although the argument is by now largely settled in favor of the judicious use of friendship, the charge continues to be made. Programming books still appear on the market describing friend declarations as indicators of poor design [2], and respected C++ experts say things like "[I avoid] friendship like the plague," [3] and "It is always best to use a friend declaration with a guilty conscience" [4].

One factor in this distrust is friendship's lack of granularity: A class grants its friends access to every detail of its implementation, which seems to violate the spirit, if not the letter, of encapsulation. Of course, the whole point of friendship is to allow coupling when appropriate; but the access control tools provided by C++ are conspicuously absent from the class-friend relationship, and without them the coupling can easily become too tight. This has long been seen as a weakness of the language, and proposals to address it have been considered and abandoned in the past [5]. Because of this weakness, it is essential to understand how to use friendship in a manner consistent with good design.

This article summarizes the generally accepted rules for the use of free friend functions, and suggests some guidelines for the use of friend classes and class members. It also addresses the question of which class members a friend should be allowed to use (in other words, the characteristics of the "private interface" that should be offered to friends). Finally, it describes the "Attorney-Client" idiom, a lightweight mechanism that helps a class specify this private interface, thereby enabling it to create contracts with its friends.

Guidelines for Friend Functions

Scott Meyers has developed a straightforward algorithm [6, 7] for deciding, given a class and a function, whether the function should be implemented as a class member, a free friend function, or a free nonfriend function. It is reproduced here in a slightly modified form (see Figure 1).

Examining the single case where the function should be implemented as a free friend, it's clear that friendship is appropriate only in the case where (a) the expected usage of the function dictates that it be a free function; and (b) the function cannot be implemented using just the public interface of the class. (For a similar but less formal treatment of this topic, see the accompanying sidebar entitled "The Three Ps.")

Guidelines for Friend Classes and Class Members

In the interest of brevity, I will begin by defining some terms:

  • Grantor: A class granting friendship, either to a free function, a class member, or a whole class.
  • Friend Method: A class member function that has been declared as a friend by some grantor.
  • Friendly Class: A friend class, or one containing one or more friend methods.

Now, observe that "friend class" is simply shorthand notation for a class whose members are all declared friends [10]. This reduces the problem to determining whether an individual class member should be declared a friend method. There are three general cases where this question might arise:

  1. You are designing two or more classes to work together collaboratively.
  2. You discover (during maintenance or feature enhancement) that one class "needs" access to some private member data of another.
  3. You discover that one class "needs" access to some private member function(s) of another.

Note the quotes around the word "needs." Most of the decision about using friendship hinges on just how real that need turns out to be.

Upon reflection, it should be evident that the first case is really a special case of the second and third. The only difference is that if you begin with the assumption that your classes will be more closely coupled, you will probably be more predisposed to assume that the need is real. Other than that, the exact same principles apply.

Similarly, the second case is identical to the third. Conceptually, any access to a data member of a class is equivalent to using an inline accessor (set or get) function. In fact, in the case of (potential) friendship, an accessor function is the preferred mechanism (for reasons that will become clear below). So, when you consider how your potential friend methods might access the private data of the grantor, imagine them using inline accessor functions.

The problem can now be restated as: Given classes F and G, where F has some member function foo() that needs to call a private member p() of G, what should you do? Your choices are:

  1. Make G::p() public;
  2. Refactor your design;
  3. Make F:foo() a friend of G.

Due to the combination of bias against friendship and reluctance to refactor code, it's a safe bet that number 1 is the most frequently chosen option. We've all done it—even you. It's also usually the worst option of the three. Rather than giving your classes the capabilities that they need, or weakening encapsulation by coupling two unrelated classes, making G::p() public compromises the entire public interface of G. This has negative consequences for documentation and testing, just for starters, and is an invitation to all sorts of maintenance problems down the road. Arguably, encapsulation is more broken in this case than it would be by declaring F::foo() a friend.

Of course, sometimes making G::p() public is the right answer. Ask the same questions you would anytime you consider adding a public function to a class: Does it provide an essential service to clients (other than F::foo())? Is its usage consistent with that of the other public members? Does it sufficiently hide G's implementation details? If you can honestly say that p() belongs in G's public interface, then congratulations! You are done.

In most cases, though, you must at least consider refactoring. A full treatment of the topic is beyond the scope of this article, but your choices are likely to involve either moving foo() into G, moving p() into F, or moving both foo() and p() into some other class. There are several well-known refactorings that may prove useful, including Move Method, Move Field, Extract Class, and Remove Middle Man [11]. However, it's important to realize that in many cases, refactoring, though desirable, may not be feasible. Particularly with legacy code, factors such as time constraints, code complexity, and lack of unit tests may all be compelling arguments against attempting to refactor (at least in the near term).

So if you don't want to add a public member function, and refactoring is not an option, friendship is the only alternative left. The question, then, is how to minimize the coupling between the grantor and its friend(s). Here are a few guidelines:

  • Only private, nonvirtual members of a friendly class should be declared as friend methods.
  • Private: Consider the various access levels (public, protected, and private) as "interfaces" to various entities. Public member functions represent the external interface to unrelated classes. Protected members represent the interface to derived classes (if the friendly class is to be used as a base class). Private members are the implementation. Neither public nor protected friend methods should have direct access to the grantor's implementation because it will make them more fragile in the face of changes to that implementation. Furthermore, the goal is to keep public and protected interfaces small (as large as necessary, but no larger); so if a friend method is added to the class, it clearly should not be public or protected. Finally, this preserves the principle of friendship as an implementation detail [12]: It should be possible to change the signature of a friend method without affecting the public or protected interfaces; clearly this cannot happen if the friend method is a member of one of those interfaces.
  • Nonvirtual: If the friendly class is not intended to be a base class, it simply shouldn't have any virtual functions. If it is to be a base class, there is no benefit in declaring its virtual members as friends: Friendship is not inherited, so an override of a virtual friend function would not have access to the grantor's private members.
  • A friend should only be granted access to private member functions (virtual or nonvirtual) of the grantor.
  • Private: Again, consider the interfaces. For the grantor, the public and protected member functions still represent the interfaces to clients and subclasses, respectively. The private member functions serve both as the implementation and the interface to friends. Consider also the degree of coupling: The public interface implies the lowest degree of coupling (no reliance on implementation details); the protected interface implies more coupling (reliance on lower level member functions, but no access to member data, assuming the class has only private data); the private interface is tightly coupled (allowing access to all implementation details).
  • Member functions, not member data: Adhering to this rule enables the creation of a contract between the grantor and the friend, providing the same benefits as contracts in the public interface—hiding of implementation details, providing read-only access to the object state via const functions, looser coupling, and so on.
  • Virtual vs. nonvirtual: This is a design decision with its own set of trade-offs. There are at least two common uses of private virtual functions. One is in implementing the Template Method design pattern [13]; friends are neither required nor precluded from use in this context. The other is the Virtual Friend Function idiom [14], where a nonvirtual friend function is made to act virtual by calling a virtual function in the grantor. Granting access to virtual functions will increase the coupling with the friend (it may call overrides of those functions), so the design of subclasses must take this possibility into account. But in some situations, this will be a reasonable price to pay for the benefits gained.

Enforcing the Contract

I referred above to providing friends with access only to the private, nonvirtual member functions of the grantor, and of establishing a contract between the grantor and its friends. How is this possible, given that friendship in C++ is an all-or-nothing proposition? One way is to use the Attorney-Client idiom, which allows grantors to precisely control the amount of access they give their friends.

The implementation is quite simple (see Listing 1). In the grantor class, declare a friend class to serve as the grantor's "Attorney." The Attorney class implementation is entirely private and consists only of inline static member functions, each taking a reference to an instance of the grantor class as its first argument and calling a corresponding member function on the grantor instance with the remaining arguments. It also declares a set of friends (free functions, classes, and class member functions) that will be allowed access to its implementation.

In effect, the Attorney acts as a middleman, defining an interface that serves as a proxy for the grantor, and declaring (as its own friends) who will be allowed to use the interface. Only those functions can use the Attorney's private implementation to access the private members of the grantor. Like a real-life attorney, it knows all of its client's secrets, but it only shares some of them with a small number of outsiders.

Variations

Some variations on this idiom can be useful for specific cases. For instance, a "Corporate Counsel" class may function as the Attorney for several related classes. Its interface resembles the union of two or more individual Attorney classes. This is a natural variation to use when two or more classes need to declare an identical set of friends. Another variation is the "Consigliere," where the Attorney is a nested class of the grantor. This keeps the definition of the interface, as well as the set of friends, entirely under the control of the grantor class. Finally, just as in real life, you can use different Attorneys to represent you in different situations. If different friends need access to different sets of your private members, you can easily define a separate Attorney class for each one. Again, you will benefit from looser coupling and better encapsulation.

Performance Issues

You can have as many Attorneys as you can afford. But how many is that? What are the costs of using this idiom? Are Attorneys as expensive in this context as they are in real life?

Fortunately, there is a minimal runtime cost to using Attorneys. Because they are basically a collection of inline functions that pass through to their client objects, they are very efficient—in most cases, as efficient as directly accessing data members or calling member functions in the grantor class. Even more complex functions that take or return nontrivial objects generally give comparable results, thanks to the Return Value Optimization [15]. For a Visual C++ 6.0 project that demonstrates the overhead of using an Attorney class in various situations, see ACDemo.zip (available at http://www.cuj.com/code/).

The main cost of this idiom is increased complexity in your code. A proliferation of Attorney classes might obscure the meaning of the relationships between your grantor classes and their friends, and confuse maintainers who are unfamiliar with the idiom. However, this is a danger with any idiom, and the key to preventing it is by providing thorough documentation. And, given that friendship is an infrequently used language feature, I believe that the benefits of this technique far outweigh the costs.

Conclusion

Historically, many C++ developers have seen friendship as incompatible with strong encapsulation, and a symptom of poor design. However, when used responsibly, friendship can actually enhance encapsulation. Using friendship properly requires not only understanding when it is the appropriate solution, but also how to structure the interface between a class and its friends.

Once this interface is established, it must be enforced. Although C++ does not provide direct support for a class-friend interface, the Attorney-Client idiom can be used to create one. This idiom, which is simple to implement and efficient to use, allows a class to go beyond simply declaring a set of friends; it enables the creation of contracts with those friends, and allows classes to grant friendship without compromising their privacy.

Acknowledgments

Thanks to Darren Blaser, Patrick Harper, Vanessa Hutchison, Janice Kaltenecker, Gary Keskela, Jim Krahn, and Bill Lish for their insightful comments.

References

  1. Stroustrup, Bjarne. The Design and Evolution of C++, Addison-Wesley, 1994, p. 53.
  2. Misfeldt, Trevor, et al. The Elements of C++ Style, Cambridge University Press, 2004, p. 77.
  3. Wilson, Matthew. "Friendly Templates," C/C++ Users Journal Experts Forum, December 2003; http://www.cuj.com/ documents/s=8943/cujexp0312wilson2/.
  4. Josuttis, Nicolai M. Object-Oriented Programming in C++, John Wiley and Sons, 2001.
  5. Stroustrup, Bjarne. The Design and Evolution of C++, Addison-Wesley, 1994, pp. 55-56.
  6. Meyers, Scott. Effective C++, Second Edition, Addison-Wesley, 1998, Item 19.
  7. Meyers, Scott. "How Non-Member Functions Improve Encapsulation," C/C++ Users Journal, February 2000.
  8. Cline, Marshall, et al. C++ FAQs, Second Edition, Addison-Wesley, 1999, Item 19.11. Note that although in this item the criteria are used for determining friendship, they are really criteria for when a free function should be used. This fact is glossed over in their discussion, which centers around the notion of transforming a member function into a friend; thus they don't mention Meyers' key insight, which is that a friend is simply a "3 P" function that happens to need access to T's innards.
  9. ibid., Item 19.06.
  10. Stroustrup, Bjarne. The C++ Programming Language, Third Edition, Addison-Wesley, 1997, Section 11.5.
  11. Fowler, Martin. Refactoring: Improving the Design of Existing Code, Addison-Wesley, 2000.
  12. Lakos, John. Large Scale C++ Software Design, Addison-Wesley, 1996, p. 137.
  13. Gamma, Erich, et al. Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1995, p. 325.
  14. Cline, Marshall, et al. C++ FAQs, Second Edition, Addison-Wesley, 1999, Item 19.10.
  15. Bulka, Dov and David Mayhew. Efficient C++: Performance Programming Techniques, Addison-Wesley, 2000.

CUJ


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Comments:

Video