Lesson 4 | Friend functions |
Objective | Know when (and when not) to declare a friend function, with safe modern C++ patterns. |
friend
Functionfriend
grants a non-member function or another class access to a class’s private/protected members. This is powerful-and easy to misuse. Prefer encapsulation and narrow interfaces; introduce friendship only when it materially improves correctness or symmetry and no clean public API exists.
operator==
, arithmetic operators when implemented as non-members for symmetry. If access to internals is unavoidable, use a hidden friend (declare inside the class, define inline) to enable ADL while keeping the function non-member.operator<<(std::ostream&, const T&)
is commonly a hidden friend that reads private state for presentation.friend
.friend class X;
unless every member truly needs access. Prefer a specific friend
function.BSTR
or raw ownership channels in interface code. Use RAII types like std::string
, std::u16string
, and smart pointers. Keep operator code exception-safe and noexcept
where appropriate.friend
; prefer behavior-level tests or narrow observers. If you must, isolate test-only friends behind compile-time switches.Define the operator inside the class body (as a friend) but not as a member; this preserves symmetry and enables ADL without polluting the global namespace.
#include <ostream>
class Point {
int x_ = 0, y_ = 0;
public:
Point(int x, int y) : x_{x}, y_{y} {}
// Hidden friend: non-member, but declared here for access + ADL.
friend std::ostream& operator<<(std::ostream& os, const Point& p) {
return os << '(' << p.x_ << ", " << p.y_ << ')';
}
};
Expose a narrow observer and implement the operator without friendship.
#include <compare>
class Money {
long long cents_ = 0; // invariant: always integral cents
public:
explicit Money(long long cents) : cents_{cents} {}
long long cents() const noexcept { return cents_; } // narrow observer
};
// No friend needed:
inline Money operator+(const Money& a, const Money& b) {
return Money{a.cents() + b.cents()};
}
inline bool operator==(const Money& a, const Money& b) noexcept {
return a.cents() == b.cents();
}
inline std::strong_ordering operator<=>(const Money& a, const Money& b) noexcept {
return a.cents() <=> b.cents();
}
Operators that conceptually modify the left operand (=
, []
, ()
, ->
) should be members. Return by reference where expected.
class Buffer {
// ...
public:
Buffer& operator=(const Buffer&) = default; // return by reference
Buffer& operator+=(const Buffer& rhs) { /* ... */ return *this; }
};
Prefer a single narrow friend function instead of friend class
.
class Token {
int kind_;
int value_;
public:
// Narrow friend: only the parser's exact function needs access.
friend bool parse_token_stream(class Parser& p, Token& out);
};
friend
noexcept
/strong guarantees where appropriate)?BSTR
) and favoring RAII/smart pointers?Where do I put the declaration? Inside the class that grants access. For hidden friends, define the function inline there as well. Otherwise, declare inside, define in the same namespace as the class so ADL finds it.
Does access specifier matter? No-friend
ignores public/protected/private
placement. We place friends with related API (usually near observers) for discoverability.
friend
functions.