Friends

Part of C++ FQA Lite. To see the original answers, follow the FAQ links.

The questions here are about friend declarations. A short answer to them all: you probably don't need friend declarations.

[14.1] What is a friend?

FAQ: A class can declare other classes and/or functions as friends. The declaration allows the friends to access non-public class members unaccessible to other code outside of the class.

FQA: In other words, friend refines the access control provided by the private keyword. Which means it's almost useless, simply because private is almost useless. Specifically, it fails to provide encapsulation, and therefore is little more than a comment.

If you define interfaces between modules developed independently and want these interfaces to be stable, it's better to use C for the interface definitions, not C++. If you insist on using C++ (forcing both modules to be built with the same tools), relying on private to provide encapsulation is a bad idea, because changing private members triggers recompilation of the client code. You can use forward declarations and/or pure abstract classes to provide a more stable interface.

If you work on the internal interfaces between the different classes of a module, and you're stuck with C++, using private is surely better than defining all members public. This way, someone can easily tell that a member is not accessed outside of the class, and clarity is good. However, there is little reason to obsess with the fine details of access control in the internal interfaces.

Most of the time, the distinction between private and public is good enough to describe what you want. If you feel a strong urge to split tightly coupled functions between several classes, you can do it with friend, or you can make the members public. Of course it's possible to argue forever about this ("But it's bad design to allow everyone to access the members!", "But it's bad design to have tightly coupled classes!"). However, these are some of the internal classes of a module, nothing more, nothing less. If the module has reasonable size, the access control is not that big a deal. If the module is huge, you have a huge problem either way, especially with C++, which compiles forever and doesn't localize the damage of run time errors.

Therefore, friend is totally useless for the external interfaces and almost useless for the internal interfaces. So there you are.

[14.2] Do friends violate encapsulation?

FAQ: On the contrary - they enhance it. If used properly, of course.

For example, you can use them to split code into two classes and have their data inaccessible for code in all other classes. Or you can use them as "a syntactic variant" of member functions.

Think of the friends as part of the class interface instead of something outside the class.

FQA: What encapsulation? C++ doesn't have encapsulation. It has access control (code outside of a class using private members of this class will not compile), but it has neither compile time encapsulation (stable binary interfaces) nor run time encapsulation (safe memory access). What's there to violate?

The case of two classes was discussed in the previous FAQ; this argument only sounds convincing if you think C++ classes are a good way to define the important, stable interfaces in your system.

As to the "syntactic variant" business, it's probably about overloaded operators. The C++ syntax makes it impossible to define overloaded operators as class members unless the first argument is an object of the class. Since C++ uses stream<<obj to print things, many classes declare a friend operator<<, for example. This argument is only interesting if you think C++ operator overloading is any good.

[14.3] What are some advantages/disadvantages of using friend functions?

FAQ: Advantage: they allow the designer to choose between obj.func() and func(obj), which "lowers maintenance costs".

Disadvantage: they require more code to achieve dynamic binding. Non-member functions can't be virtual, so if the designer's syntax of choice is func(obj), and dynamic binding is needed, func will have to delegate to a protected virtual member with obj.func().

FQA: Listen carefully, this is an excellent opportunity to learn about the C++ approach to writing software. Choosing between two equivalent syntactic forms is called "design". The availability of many forms and the "right" choice between them is expected to "lower maintenance costs". Does this make any sense at all? Have you seen headlines such as "An engineer replaced obj.func() with func(obj), profits sky-rocket" or "A researcher wins the Turing award for discovering a new way to replace obj.func() with func(obj)"?

And why not say out loud that the real choice is between obj<<stream and stream<<obj or something like this? This is really about operator overloading, because that's where the member/global function distinction matters most syntactically. Why not just say obj.print(stream) instead of shifting streams by the amount of bits in an object or something?

[14.4] What does it mean that "friendship isn't inherited, transitive, or reciprocal"?

FAQ: It means that classes derived from a friend class don't automatically become friends (do you trust the kids of your friends?), a friend of a friend doesn't automatically become a friend (do you trust the friends of your friends?), and that a class declaring another class as "friend" doesn't automatically become a friend of that class (do you trust anyone who calls you a friend?).

FQA: The real life analogies are too scary for a detailed discussion. "Do your friends have access to your members"?

One reason to avoid friends is that the only possible benefit is a little extra clarity, but this benefit can be dwarfed by the extra obfuscation - not everyone is (or should be) 100% sure what friend means when it comes to class hierarchies and stuff.

[14.5] Should my class declare a member function or a friend function?

FAQ: Use a member function unless you must use a friend function. For example, you may need friend for operator overloading.

FQA: The FAQ says it at last! See? It was about operator overloading all the way.

The FAQ's advice may be further simplified if we use the observation that C++ operator overloading is just a pain in the neck.


Copyright © 2007-2009 Yossi Kreinin
revised 17 October 2009