Skip to content
C++ Better Explained
Go back
Friend Functions in C++: What They Are and When to Use Them

Friend Functions in C++: What They Are and When to Use Them

Private members exist so that outside code can’t touch them. A friend function is how a class makes a deliberate exception — it names a specific outside function and says “this one is allowed in.”

It sounds like a loophole in encapsulation. It isn’t, and the reason why is worth understanding.


The Problem Friends Solve

Say you have a Temperature class and you want to print it with std::cout:

Temperature t(23.5);
std::cout << t;   // we want this to work

To make that work, you need to overload operator<<. But here’s the catch: the left operand is std::cout, not your object. That means the overload cannot be a member function of Temperature — member functions always take the object on the left.

So it has to be a free function. But a free function can’t read Temperature’s private data. That’s exactly the gap friend fills.


Basic Syntax

#include <iostream>

class Temperature {
private:
    double celsius;

public:
    Temperature(double c) : celsius(c) {}

    // Grant access — this is a declaration, not a member function
    friend std::ostream& operator<<(std::ostream& os, const Temperature& t);
};

// Defined outside the class, like a normal function.
// No "Temperature::" prefix — it is NOT a member.
std::ostream& operator<<(std::ostream& os, const Temperature& t) {
    os << t.celsius << "C";   // legal: we are a friend
    return os;
}

int main() {
    Temperature today(23.5);
    std::cout << "Today: " << today << "\n";
    return 0;
}

Output:

Today: 23.5C

Three things to notice:


Friend Functions for Binary Operators

The other classic use is symmetric operators. Consider adding two vectors:

#include <iostream>

class Vec2 {
private:
    double x, y;

public:
    Vec2(double x, double y) : x(x), y(y) {}

    friend Vec2 operator+(const Vec2& a, const Vec2& b);
    friend bool operator==(const Vec2& a, const Vec2& b);
    friend std::ostream& operator<<(std::ostream& os, const Vec2& v);
};

Vec2 operator+(const Vec2& a, const Vec2& b) {
    return Vec2(a.x + b.x, a.y + b.y);
}

bool operator==(const Vec2& a, const Vec2& b) {
    return a.x == b.x && a.y == b.y;
}

std::ostream& operator<<(std::ostream& os, const Vec2& v) {
    os << "(" << v.x << ", " << v.y << ")";
    return os;
}

int main() {
    Vec2 a(1.0, 2.0);
    Vec2 b(3.0, 4.0);

    std::cout << a << " + " << b << " = " << (a + b) << "\n";
    std::cout << "a == b? " << ((a == b) ? "yes" : "no") << "\n";

    return 0;
}

Output:

(1, 2) + (3, 4) = (4, 6)
a == b? no

You could write operator+ as a member function. But then the two operands are treated asymmetrically — the left one is the object, the right one is a parameter. As a free friend function, both sides are plain parameters, which reads better and behaves more predictably with implicit conversions.

If you're looking to go deeper with C++, the C++ Better Explained Ebook is perfect for you — whether you're a complete beginner or looking to solidify your understanding. Just $19.

Friend Classes

You can also make an entire class a friend, granting all of its member functions access:

#include <iostream>

class Engine {
private:
    int rpm;
    bool running;

    void ignite() { running = true; rpm = 800; }

public:
    Engine() : rpm(0), running(false) {}

    friend class Car;   // Car may touch Engine's privates
};

class Car {
private:
    Engine engine;

public:
    void start() {
        engine.ignite();          // reaching into a private method
        std::cout << "Engine started at " << engine.rpm << " rpm\n";
    }

    void status() const {
        std::cout << "Running: " << (engine.running ? "yes" : "no") << "\n";
    }
};

int main() {
    Car car;
    car.status();
    car.start();
    car.status();
    return 0;
}

Output:

Running: no
Engine started at 800 rpm
Running: yes

Friend classes are a bigger hammer than friend functions — you’re granting access to every member function, present and future. Use them when two classes are genuinely two halves of one design (a container and its iterator is the textbook example), not just because it’s convenient.


The Three Rules of Friendship

Friendship in C++ is narrower than most people assume:

It is not mutual. If Engine declares Car a friend, Car does not automatically declare Engine a friend. Access flows one way only.

It is not inherited. If Car is a friend of Engine, a class SportsCar derived from Car is not a friend of Engine. Each class must be named explicitly.

It is not transitive. If A befriends B and B befriends C, then C has no access to A.

These restrictions are deliberate. They keep the set of code that can touch your private data small and explicitly listed in one place — the class definition.


When You Should Not Use Friend

Reach for friend sparingly. Ask these questions first:

Would a public getter work? If the data is safe to expose read-only, a const getter is simpler and doesn’t couple an external function to your internals.

Should this be a member function instead? If the left-hand operand is naturally your object, make it a member. Operators like +=, [], =, and () must be members anyway.

Are you using it to patch a design problem? If two classes constantly need each other’s privates, that’s often a sign they should be one class, or that a third abstraction is missing.

The healthy use cases are narrow and well known: stream operators, symmetric binary operators, and tightly-paired class designs.


Summary

Friend functionMember functionPublic getter
Access to privatesYesYesVia the getter only
Left operand must be the objectNoYesN/A
Works for operator<<YesNoYes, but verbose
CouplingModerateHigh (it is the class)Low

A friend function is an outside function that the class explicitly invites in. Because the invitation is written in the class itself, encapsulation stays intact — the class still decides who gets access.



Take Your C++ Further

If you’re looking to go deeper with C++, the C++ Better Explained Ebook is perfect for you — whether you’re a complete beginner or looking to solidify your understanding. Just $19.

👉 Get the C++ Better Explained Ebook — $19

📋

Free Download: The 10 Mistakes Every C++ Beginner Makes

A free 1-page checklist that shows the exact traps that slow down every C++ beginner — so you can avoid them from day one.

🔒 No spam. Unsubscribe anytime.


Share this post on:

Written by

Sahil Bora

Software Engineer. Author and creator of C++ Better Explained.


Previous Post
Binary Search in C++: Iterative, Recursive, and std::binary_search
Next Post
Function Pointers in C++: A Beginner's Guide with Examples

Keep Learning