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.

IT Services Circle
IT Services Circle
IT Services Circle
Master C++ friend: 5 Levels of Usage Every Interview Candidate Should Know

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.

声明:本文是经过严格查阅相关权威文献和资料,形成的专业的可靠的内容。全文数据都有据可依,可回溯。特别申明:数据和资料已获得授权。本文内容,不涉及任何偏颇观点,用中立态度客观事实描述事情本身。
design-patternsaccess controlCfriend
IT Services Circle
Written by

IT Services Circle

Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.