-
Notifications
You must be signed in to change notification settings - Fork 17
/
friend.fqa
105 lines (73 loc) · 6.28 KB
/
friend.fqa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
Friends
{'section':14,'faq-page':'friends.html'}
The questions here are about |friend| declarations. A short answer to them all: you probably don't need |friend| declarations.
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 [7.4 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 [6.3 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 [7.5 forward declarations] and\/or [20.1 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.
-END
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 [14.1 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 [13.1 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.
-END
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?
-END
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.
-END
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 [14.3 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.
-END