C++ : Friends




PART 6

Q24: What is a `friend'?
A: Friends can be either functions or other classes.  The class grants friends
unlimited access privileges.



Q25: Do `friends' violate encapsulation?
A: Friends can be looked at three ways: (1) they are not class members and they
therefore violate encapsulation of the class members by their mere existence,
(2) a class' friends are absorbed into that class' encapsulation barrier, and
(3) any time anyone wants to do anything tricky they textedit the header file
and add a new friend so they can get right in there and fiddle 'dem bits.

No one argues that (3) is a Good Thing, and for good reasons. The arguments for
(1) always boil down to the rather arbitrary and somewhat naive view that a
class' member functions `should' be the *only* functions inside a class'
encapsulation barrier.  I have not seen this view bear fruit by enhancing
software quality.  On the other hand, I have seen (2) bear fruit by lowering
the *overall* coupling in a software system.  Reason: friends can be used as
`liaisons' to provide safe, screened access for the whole world, perhaps in a
way that the class syntactically or semantically isn't able to do for itself.

Conclusion: friend functions are merely a syntactic variant of a class' public
access functions.  When used in this manner, they don't violate encapsulation
any more than a member function violates encapsulation.  Thus a class' friends
and members *are* the encapsulation barrier, as defined by the class itself.

I've actually seen the `friends always violate encapsulation' view *destroy*
encapsulation: programmers who have been taught that friends are inherently
evil want to avoid them, but they have another class or fn that needs access to
some internal detail in the class, so they provide a member fn which exposes
the class' internal details to the PUBLIC!  Private decisions should stay
private, and only those inside your encapsulation barrier (your members,
friends, and [for `protected' things] your subclasses) should have access.



Q26: What are some advantages/disadvantages of using friends?
A: The advantage of using friends is generally syntactic.  Ie: both a member fn
and a friend are equally privileged (100% vested), but a friend function can be
called like f(obj), where a member is called like obj.f().  When it's not for
syntactic reasons (which is not a `bad' reason -- making an abstraction's
syntax more readable lowers maintenance costs!), friends are used when two or
more classes are designed to be more tightly coupled than you want for `joe
public' (ex: you want to allow class `ListIter' to have more privilege with
class `List' than you want to give to `main()').

Friends have three disadvantages.  The first disadvantage is that they add to
the global namespace.  In contrast, the namespace of member functions is buried
within the class, reducing the chance for namespace collisions for functions.

The second disadvantage is that they aren't inherited.  That is, the
`friendship privilege' isn't inherited.  This is actually an advantage when it
comes to encapsulation.  Ex: I may declare you as my friend, but that doesn't
mean I trust your kids.

The third disadvantage is that they don't bind dynamically.  Ie: they don't
respond to polymorphism.  There are no virtual friends; if you need one, have a
friend call a hidden (usually `protected:') virtual member fn.  Friends that
take a ptr/ref to a class can also take a ptr/ref to a publically derived class
object, so they act as if they are inherited, but the friendship *rights* are
not inherited (the friend of a base has no special access to a class derived
from that base).



Q27: What does it mean that `friendship is neither inherited nor transitive'?
A: This is speaking of the access privileges granted when a class declares a
friend.

The access privilege of friendship is not inherited:
 * I may trust you, but I don't necessarily trust your kids.
 * My friends aren't necessarily friends of my kids.
 * Class `Base' declares f() to be a friend, but f() has no special access
   rights with class `Derived'.

The access privilege of friendship is not transitive:
 * I may trust you, and you may trust Sam, but that doesn't necessarily mean
   that I trust Sam.
 * A friend of a friend is not necessarily a friend.



Q28: When would I use a member function as opposed to a friend function?
A: Use a member when you can, and a friend when you have to.

Like in real life, my family members have certain privileges that my friends do
not have (ex: my family members inherit from me, but my friends do not, etc).
To grant privileged access to a function, you need either a friend or a member;
there is no additional loss of encapsulation one way or the other.  Sometimes
friends are syntactically better (ex: in class `X', friend fns allow the `X'
param to be second, while members require it to be first).  Another good use of
friend functions are the binary infix arithmetic operators.  Ex: `aComplex +
aComplex' probably should be defined as a friend rather than a member, since
you want to allow `aFloat + aComplex' as well (members don't allow promotion of
the left hand arg, since that would change the class of the object that is the
recipient of the message).