8.3 Friends
private members are protected from outside code. That protection is good: it keeps the object in charge of its own rules. But C++ also has a feature called friend, which lets a class grant selected outside functions or classes permission to access its private and protected members.
Friendship is not a shortcut for "I do not want to write getters." It is an explicit design decision: "this outside function is so closely related to my class that it belongs beside the class interface, even though it is not a member function."
Why a friend can be useful
Suppose a Point hides its coordinates. A normal outside function cannot read x_ and y_.
class Point {
private:
double x_;
double y_;
public:
Point(double x, double y) : x_(x), y_(y) {}
};
double distance(const Point& a, const Point& b) {
// Error: x_ and y_ are private.
// return std::sqrt((a.x_ - b.x_) * (a.x_ - b.x_) + ...);
}You could add getters, and that is often the right answer. But distance between two points is a symmetric operation: neither point is more important than the other. A free function reads naturally:
double d = distance(start, finish);In this case, Point can authorize the free function.
#include <cmath>
class Point {
private:
double x_;
double y_;
public:
Point(double x, double y) : x_(x), y_(y) {}
friend double distance(const Point& a, const Point& b);
};
double distance(const Point& a, const Point& b) {
double dx = a.x_ - b.x_;
double dy = a.y_ - b.y_;
return std::sqrt(dx * dx + dy * dy);
}distance is still not a member function. It has no this pointer, and you call it as distance(a, b), not a.distance(b). The class simply says: this one function may read my private data.
Friendship is specific, one-way, and not inherited
Friendship is more precise than many beginners expect:
- If
Pointdeclaresdistanceas a friend, only that exact function gets access. - If
PointbefriendsInspector, that does not meanInspectorautomatically befriendsPoint. - If a base class declares a friend, derived classes do not automatically grant the same access to their own private members.
This is why friend declarations are powerful but also readable: the access grant is visible in the class definition.
class SecretBox {
private:
int code_;
public:
explicit SecretBox(int code) : code_(code) {}
friend class SecurityAudit;
};
class SecurityAudit {
public:
void inspect(const SecretBox& box) const {
std::cout << "internal code: " << box.code_ << std::endl;
}
};Here SecurityAudit can read SecretBox::code_. Ordinary code still cannot. If every part of the program needs the code, it should be a public query instead. If only a narrow auditing tool needs it, friend can express that narrow relationship.
Friend functions versus public getters
Before writing friend, ask what interface you really want:
class Wallet {
private:
int cents_;
public:
int cents() const { return cents_; }
};A getter is good when the value is part of the public meaning of the type. For example, a Wallet may reasonably expose the number of cents.
Friendship is better when the operation needs internal details but you do not want to expose those details to everyone.
class Box {
private:
int width_;
int height_;
int depth_;
public:
Box(int width, int height, int depth)
: width_(width), height_(height), depth_(depth) {}
friend bool same_volume(const Box& left, const Box& right);
};
bool same_volume(const Box& left, const Box& right) {
return left.width_ * left.height_ * left.depth_
== right.width_ * right.height_ * right.depth_;
}This keeps the dimensions private while still allowing one carefully chosen comparison. The tradeoff is coupling: a friend knows the class internals, so changing private representation may require changing that friend too.