Ch13.10: friend: Letting One Outsider In
1. The Operation That Belongs to the Type but Cannot Be a Member
Sometimes the operation you need takes two objects of the class and treats them as equals. The midpoint of two points, the distance between two points, the sum of two vectors: none of them is “a method of the left-hand object”, and none of them can be written outside the class either, because the data it needs is private.
friend is how the class gives one named outsider the
access its own member functions already have. The permission is granted
inside the class, by the class that owns the data:
class point
{
public:
friend point midpoint(point const &a, point const &b) noexcept; // granted here
private:
::std::int_least32_t x_{};
::std::int_least32_t y_{};
};
The friend line is a declaration of an ordinary free
function that happens to be allowed to look inside. It is not a member: it has
no this, it is not called with an object, and it is not part of the
class’s list of member functions.
2. A Friend Function and a Friend Class
#include <fast_io.h>
#include <cstdint>
using namespace ::fast_io::io;
class point
{
public:
point(::std::int_least32_t x, ::std::int_least32_t y) noexcept : x_{x}, y_{y}
{
}
::std::int_least32_t sum() const noexcept
{
return x_ + y_;
}
// One named function may look inside.
friend point midpoint(point const &a, point const &b) noexcept;
// A whole class may look inside.
friend class inspector;
private:
::std::int_least32_t x_{};
::std::int_least32_t y_{};
};
point midpoint(point const &a, point const &b) noexcept
{
return point{(a.x_ + b.x_) / 2, (a.y_ + b.y_) / 2};
}
class inspector
{
public:
static void dump(char const *label, point const &p) throws
{
println(::fast_io::mnp::os_c_str(label), ": x = ", p.x_, ", y = ", p.y_);
}
};
int main()
{
try
{
point a{0, 0};
point b{10, 20};
auto m{midpoint(a, b)};
println("midpoint.sum() = ", m.sum());
inspector::dump("midpoint", m);
}
catch throws (::std::error e)
{
print("failed: ", e, "\n");
}
}
midpoint.sum() = 15
midpoint: x = 5, y = 10
Two grants, two shapes:
-
friend point midpoint(point const &, point const &)— one free function may readx_andy_, and may use the public constructor. Nothing else changes. -
friend class inspector;— every member function ofinspectormay read them, including the staticdumpused above. The grant is to the class as a whole, exactly asprivateis granted to a class as a whole (Ch13.4).
midpoint(a, b) is callable at all.
A function declared only inside a class as a friend is invisible to
ordinary name lookup, but it is found when one of its arguments is of the class
type — that is argument-dependent lookup, and it is why
midpoint(a, b) resolves without a second declaration at namespace
scope. If you want to be able to call it with the class type nowhere in the
arguments, declare it at namespace scope as well.
3. What friend Is Not
| People expect | What actually happens |
|---|---|
| “Friendship goes both ways.” | It does not. If inspector is a friend of
point, then inspector may look inside a
point — but point gains nothing, and
inspector’s own private members are still private to
point. |
| “A friend of my friend is my friend.” | It is not transitive. inspector being a friend of
point says nothing about any other class. |
| “I can declare myself a friend.” | You cannot. Only the class that owns the data can grant access to it. Friendship is given, never taken. |
| “A friend is a member.” | It is not. There is no this inside a friend function, and
it is not listed among the class’s member functions. |
4. Reach for It Last
friend is the narrowest hole you can open in the wall, and it is
still a hole. Before granting one, check the cheaper options in this order:
-
Can a member function do it? If the operation has a natural
subject object, make it a member.
midpointhas no natural subject — that is why it is not one. -
Can the public interface do it? A friend function built on
public accessors (
a.x(),a.y()) needs no grant at all. Use that whenever the accessors already exist; it keeps the class closed and the function testable. - Is it about the class, not an object? Then a static member function is the right shape (Ch13.9).
-
Otherwise — symmetric operations between two objects
are the classic case —
friendis the tool.
friend creates coupling.
A friend function is now part of the class’s implementation: change the
data members and the friend breaks, even though it is not visibly in the class.
That is acceptable for the two or three operations that genuinely belong to the
type’s interface, and it is a maintenance trap if you hand out friendship
to whatever needs a shortcut. If several outsiders need the same data, the
design question is usually whether that data should have a public accessor
— not whether to add a fifth friend.
+ or == that takes two objects of the class has the
same shape as midpoint, and is usually declared as a
friend for the same reason. Operators are covered later in the
tutorial, once you have classes and references in hand.