Master C++ friend: 5 Levels of Usage Every Interview Candidate Should Know
This article walks through the five practical levels of using C++ friend— from a quick syntax recap to operator overloads, factory functions, iterator access, swap idiom, and the Passkey pattern—explaining when and why each scenario warrants a friend declaration and how it impacts encapsulation and interview performance.
Level 0: 30‑second friend syntax recap
Friend can be declared as a friend function or a friend class . The function gains access to private members, while the whole class is granted that privilege.
class MyClass {
int secret_ = 42;
// friend function
friend void peek(const MyClass& obj);
// friend class
friend class OtherClass;
};
void peek(const MyClass& obj) {
std::cout << obj.secret_ << std::endl; // OK, friend function can access
}The core idea is that friend is a directional authorization —it gives a specific function or class access to private members without exposing them publicly.
C++11 added two extensions: you can omit the class keyword when declaring a friend class, and you can make a template parameter a friend.
template<typename T> class Container {
friend T; // C++11 only
int data_;
};Level 1: operator<< and operator>> overloads
The classic interview scenario: overloading operator<< for output. Because the left operand is an ostream, the overload must be a non‑member function, yet it needs to read private data. Declaring it as a friend solves this.
class Point {
double x_, y_;
public:
Point(double x, double y) : x_(x), y_(y) {}
friend std::ostream& operator<<(std::ostream& os, const Point& p) {
return os << "(" << p.x_ << ", " << p.y_ << ")";
}
};Using a public getter would expose every member to everyone; a friend keeps the encapsulation tighter.
Level 2: Factory functions and private constructors
When a class hides its constructor to control creation, a factory function needs to call that private constructor. Making the factory a friend grants it the required access.
class DatabaseConnection {
std::string host_;
int port_;
DatabaseConnection(const std::string& host, int port) : host_(host), port_(port) {}
friend DatabaseConnection createConnection(const std::string& config);
};
DatabaseConnection createConnection(const std::string& config) {
// parse config …
return DatabaseConnection("localhost", 3306);
}A similar pattern appears in Singleton variants where the base class must invoke a private constructor of a derived class, so the base is declared as a friend of the derived.
Level 3: Iterator ↔ container relationship
Custom containers often need iterators that walk internal nodes. Exposing those nodes via public getters would break encapsulation, so the container declares the iterator class as a friend.
template<typename T> class MyListIterator;
template<typename T> class MyList {
struct Node { T data; Node* next; };
Node* head_ = nullptr;
friend class MyListIterator<T>;
public:
using iterator = MyListIterator<T>;
iterator begin() { return iterator(head_); }
iterator end() { return iterator(nullptr); }
};
template<typename T> class MyListIterator {
typename MyList<T>::Node* current_;
public:
explicit MyListIterator(typename MyList<T>::Node* node) : current_(node) {}
T& operator*() { return current_->data; }
MyListIterator& operator++() { current_ = current_->next; return *this; }
bool operator!=(const MyListIterator& other) const { return current_ != other.current_; }
};The rule of thumb: use a friend class when two classes are two views of the same abstraction (container vs iterator).
Level 4: friend swap – the hidden best practice
Declaring a non‑member swap as a friend enables Argument‑Dependent Lookup (ADL) to find the efficient, field‑wise swap used by the copy‑and‑swap idiom.
class Buffer {
size_t size_;
char* data_;
public:
Buffer(size_t size) : size_(size), data_(new char[size]) {}
~Buffer() { delete[] data_; }
friend void swap(Buffer& a, Buffer& b) noexcept {
using std::swap;
swap(a.size_, b.size_);
swap(a.data_, b.data_);
}
Buffer& operator=(Buffer other) {
swap(*this, other);
return *this;
}
};Because standard algorithms use using std::swap; swap(a,b);, a member swap would be ignored by ADL and fall back to the generic three‑move version, which is slower.
Level 5: Passkey idiom – fine‑grained replacement for friend
Friend grants access to *all* private members, which can be too broad. The Passkey pattern introduces a tiny “key” class that only the authorized creator can construct, allowing selective access.
class FactoryKey {
friend class Factory;
FactoryKey() {}
FactoryKey(const FactoryKey&) = default;
};
class Widget {
public:
Widget(int value, FactoryKey) : value_(value) {}
private:
int value_;
};
class Factory {
public:
static Widget create(int value) {
return Widget(value, FactoryKey{}); // only Factory can make the key
}
};The constructor remains public (so IDEs show it), but without a valid FactoryKey the call is ill‑formed, achieving function‑level access control.
When to choose friend function vs friend class
Use a friend function when only a single function needs access; use a friend class when an entire class’s multiple methods need to see the internals. Prefer the smaller scope of a friend function to avoid over‑exposing internals.
Summary of the five levels
L0 : Quick syntax recap – friend function vs friend class.
L1 : Operator overloads ( operator<<, operator>>) that must be non‑member but need private access.
L2 : Factory functions / Singleton – private constructors accessed via friend.
L3 : Container ↔ iterator – friend class for tightly coupled dual‑view designs.
L4 : Friend swap – enables ADL and efficient copy‑and‑swap.
L5 : Passkey idiom – fine‑grained, function‑level access control as a refined alternative to broad friend classes.
If a candidate can walk through these levels and explain why each scenario needs a friend, the interview question turns from a trivial point‑score into a strong differentiator.
声明:本文是经过严格查阅相关权威文献和资料,形成的专业的可靠的内容。全文数据都有据可依,可回溯。特别申明:数据和资料已获得授权。本文内容,不涉及任何偏颇观点,用中立态度客观事实描述事情本身。
IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
