| 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);
};
friendnoexcept/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.