Friend Functions & Friend Classes
A friend declaration lets a class grant a chosen function or class direct access to its private and protected members — a deliberate, scoped exception to encapsulation rather than a weakening of it.
Normally, only a class's own member functions can reach its private and protected members. A friend declaration is an explicit exception a class writes into itself: it names a specific function or class that may also reach in directly. Friendship is granted by the class being accessed, never taken by the accessing code, and it exists mainly for operations — like certain operators — that are conceptually tied to a class's internals but awkward or impossible to express as ordinary member functions.
1. What Is a Friend Function or Friend Class?
A friend of a class is a function or another class that the class has explicitly authorized to access its private and protected members, even though the friend is not itself a member. The friend keyword appears inside the class that is doing the granting.
printWidth is an ordinary free function, not a member of Box. It has no implicit Box object of its own and cannot be called with dot syntax on a Box (b.printWidth() would be wrong). Because Box lists it as a friend, the body of printWidth is allowed to name b.width directly, exactly as if it were reading a public member.
class Box
{
public:
explicit Box(int w) : width(w) {}
friend void printWidth(const Box& b);
private:
int width;
};
void printWidth(const Box& b)
{
std::cout << b.width << '\n';
}2. Why Friendship Exists
Encapsulation says a class's data should normally be reached only through its own member functions. That rule serves most code well, but a few operations are conceptually part of a class's behavior without naturally being a member of it — printing a class through operator<<, comparing two instances of two different classes, or a small companion class that manages the same object's internal state cooperatively. Rewriting such operations to rely only on public accessors sometimes forces a class to expose more of its internals as public than it should. Friendship offers a narrower alternative: grant just that one function, or just that one class, direct access — while everything else outside the class still sees only the public interface.
Friendship is an exception mechanism, not a design default. Reach for it after confirming that a normal member function or public accessor cannot express the operation as cleanly.
3. Declaring a Friend Function
A friend function is declared with the friend keyword followed by the function's ordinary declaration, written inside the class body. This is a declaration, not a definition — the function can be defined outside the class like any other free function, or, less commonly, defined inline inside the class body.
The friend declaration inside Account only tells the compiler that currentBalance is permitted to access Account's private members. It does not add currentBalance to Account's set of member functions, and it does not, by itself, make the name currentBalance visible outside the class the way a member declaration would — the free function still needs its own ordinary declaration to be callable from other code (which the class body itself normally provides).
class Account
{
public:
explicit Account(double b) : balance(b) {}
friend double currentBalance(const Account& a);
private:
double balance;
};
double currentBalance(const Account& a)
{
return a.balance;
}4. Friend Function Accessing Private and Protected Members
A friend function can read and write both private and protected members of the class that befriended it, exactly as a member function could.
calibrate takes its Sensor argument by non-const reference specifically because it intends to modify reading. A friend function's access rights come from the friend declaration; whether it can also modify the object depends on ordinary parameter const-correctness, just as it would for a member function called on a const object.
class Sensor
{
public:
explicit Sensor(int r) : reading(r) {}
friend void calibrate(Sensor& s, int offset);
protected:
int reading;
};
void calibrate(Sensor& s, int offset)
{
s.reading += offset; // direct access to a protected member
}5. A Friend Function Is Not a Member Function
Because a friend function is not a member, it has no implicit this pointer and no implicit access to an object. Every object it operates on must be passed to it explicitly, as a parameter.
memberSumX reaches its own object's x implicitly (as if writing this->x) but needs other.x written out for the second object. friendSumX has no implicit object at all, so both a.x and b.x are written out. This is a direct consequence of friend functions being ordinary free functions with a special access privilege, not a new kind of member.
class Point
{
public:
Point(int xIn, int yIn) : x(xIn), y(yIn) {}
// member function: operates on the implicit object
int memberSumX(const Point& other) const
{
return x + other.x;
}
friend int friendSumX(const Point& a, const Point& b);
private:
int x;
int y;
};
int friendSumX(const Point& a, const Point& b)
{
return a.x + b.x; // both objects passed explicitly
}6. Where the friend Declaration Appears Doesn't Affect Access
public, private, and protected control which parts of a class's own interface are visible to outside code. A friend declaration is neither — it is not a member at all, so the access-specifier section it happens to be written under has no effect on it. Placing a friend declaration under private, protected, or public compiles identically.
Because the placement carries no meaning, many style guides put friend declarations in one consistent spot — often near the top of the class, just after the class name — purely for readability, not because C++ requires it there.
class Widget
{
private:
friend void inspect(const Widget& w); // works exactly the same
int state = 0;
};
void inspect(const Widget& w)
{
std::cout << w.state << '\n';
}7. Multiple Friend Functions for One Class
A class may declare as many friend functions as it needs; each is an independent grant.
class Matrix2x2
{
public:
Matrix2x2(int aIn, int bIn, int cIn, int dIn)
: a(aIn), b(bIn), c(cIn), d(dIn) {}
friend int determinant(const Matrix2x2& m);
friend Matrix2x2 transpose(const Matrix2x2& m);
private:
int a, b, c, d;
};
int determinant(const Matrix2x2& m)
{
return m.a * m.d - m.b * m.c;
}
Matrix2x2 transpose(const Matrix2x2& m)
{
return Matrix2x2(m.a, m.c, m.b, m.d);
}8. Friend Classes
A whole class, not just one function, can be made a friend. Class A declares friend class B; inside its own body, which grants every member function of B direct access to A's private and protected members.
Diagnostics::report reads Engine's private horsepower directly. Without the friend class Diagnostics; declaration inside Engine, this would be a compile-time access error.
class Engine
{
public:
explicit Engine(int h) : horsepower(h) {}
friend class Diagnostics;
private:
int horsepower;
};
class Diagnostics
{
public:
void report(const Engine& e) const
{
std::cout << "Horsepower: " << e.horsepower << '\n';
}
};9. Friend Class Grants Access to All of Its Member Functions
A friend class grant is not limited to one method — every member function of the friend class (present or later added) can access the granting class's private and protected members.
Because the grant is that broad, prefer friend class over listing many individual friend functions when several closely related functions of one companion class genuinely need this access — but weigh that breadth against point 21's guidance on when friendship is appropriate.
class Ledger
{
public:
explicit Ledger(double b) : balance(b) {}
friend class LedgerAuditor;
private:
double balance;
};
class LedgerAuditor
{
public:
void checkPositive(const Ledger& l) const
{
std::cout << (l.balance >= 0 ? "OK" : "NEGATIVE") << '\n';
}
void adjust(Ledger& l, double delta) const
{
l.balance += delta; // also allowed — same friendship grant
}
};10. Friendship Is Granted, Not Taken
Only a class itself can decide, inside its own definition, which functions or classes are its friends. No outside code can force its way into a class's private members by declaring itself a friend from the outside — there is no such syntax. This keeps control over encapsulation with the class's own author: friendship is always an opt-in the class writes for itself, never an opt-out imposed on it.
11. Friendship Is Not Symmetric
If class A declares class B a friend, that grants B's members access into A. It says nothing about the reverse — A gets no special access into B unless B separately writes its own friend class A; declaration.
class B;
class A
{
public:
friend class B; // B can access A's private members
private:
int secretA = 1;
};
class B
{
public:
void peekIntoA(const A& a) const
{
std::cout << a.secretA << '\n'; // OK — granted by A
}
private:
int secretB = 2;
};
// A has NO access to B::secretB — A did not receive that friendship,
// and B never declared "friend class A;"12. Friendship Is Not Transitive
If A befriends B, and B separately befriends C, that does not make A a friend of C, nor C a friend of A. Each friend declaration is a direct, one-time grant between exactly the two names written down — it never chains through an intermediate class.
13. Friendship Is Not Inherited
If a base class declares a friend, that friend's special access applies to the base class's own members. A derived class does not automatically extend that same friendship to the members it adds — a friend of Base is not, by that fact alone, a friend of Derived. Likewise, a class that is a friend of Derived is not automatically a friend of Derived's base class. Each class controls its own friend list independently, and inheritance does not propagate it in either direction.
14. Declaring a Specific Member Function of Another Class as a Friend
Friendship can be narrowed even further than a whole class — a class can grant access to just one named member function of another class, leaving that other class's remaining member functions with ordinary (no) access. This usually requires the other class to be declared (and its member function's signature known) beforehand.
Calibrator::describe has no special access to Meter — only the specific member function named in Meter's friend declaration, Calibrator::adjust, does.
class Meter; // forward declaration
class Calibrator
{
public:
void adjust(Meter& m, int delta) const; // defined after Meter is complete
void describe() const { std::cout << "Calibrator\n"; }
};
class Meter
{
public:
explicit Meter(int r) : reading(r) {}
friend void Calibrator::adjust(Meter& m, int delta) const;
private:
int reading;
};
void Calibrator::adjust(Meter& m, int delta) const
{
m.reading += delta; // allowed: only this one member function was befriended
}15. Friend Functions, Friend Classes, and Forward Declarations
A friend declaration can be the first time a name is introduced — friend class Logger; inside another class both declares that Logger names a class and grants it friendship, even if Logger has not been defined yet elsewhere. Naming a specific member function as a friend (as in the previous section) is stricter: the compiler needs to have already seen that class's declaration and the member function's signature, so a plain forward declaration of the class (class Calibrator;) is not enough by itself — the member function's own declaration must be visible first.
16. Friend Functions in Operator Overloading
Operator overloading is the most common practical reason to reach for a friend function. Some operators read naturally as free functions rather than members, and a free-function operator that still needs to inspect a class's private state is a textbook use of friend.
17. Why operator<< for a Class Is Typically a Friend (or Free) Function
std::cout << myObject; is parsed as an expression whose left operand is the std::ostream (std::cout) and whose right operand is myObject. If operator<< were a member function, it would have to be a member of ostream, not of the class being printed — and a class cannot add member functions to the standard library's std::ostream. The standard solution is a free function operator<< that takes the stream by reference and the class by const reference, and, if it needs the class's private data, is declared a friend of that class.
Returning the std::ostream& lets chained calls like std::cout << a << b; keep working, the same way member-based chaining does.
class Point
{
public:
Point(int xIn, int yIn) : x(xIn), y(yIn) {}
friend std::ostream& operator<<(std::ostream& os, const Point& p);
private:
int x;
int y;
};
std::ostream& operator<<(std::ostream& os, const Point& p)
{
os << '(' << p.x << ", " << p.y << ')';
return os;
}
// std::cout << Point(3, 4) << '\n'; // prints: (3, 4)18. Symmetric Binary Operators as Friend Functions
A binary operator written as a member function only allows implicit conversions on its right-hand operand — the left-hand operand must already be exactly the class type, since it is the implicit object the member is called on. A free (optionally friend) function allows the compiler to apply an implicit conversion to either operand, which matters when the class also supports construction from another type.
If operator+ above were instead a member function of Money, the second expression (25 + Money(150)) would fail to compile, because int has no member operator+ that takes a Money.
class Money
{
public:
Money(int cents_) : cents(cents_) {} // not explicit — allows implicit conversion
friend Money operator+(const Money& lhs, const Money& rhs);
private:
int cents;
};
Money operator+(const Money& lhs, const Money& rhs)
{
return Money(lhs.cents + rhs.cents);
}
// Money total1 = Money(150) + 25; // 25 converted to Money on the RIGHT
// Money total2 = 25 + Money(150); // 25 converted to Money on the LEFT — only works
// because operator+ is a free (friend) function19. Encapsulation Implications of Friendship
Friendship does relax encapsulation, but in a controlled way: it names exactly which functions or classes get the extra access, and that grant is visible to any reader of the class's own definition. This is a narrower, more explicit exposure than making the members public — a public member is reachable by every piece of code that includes the header, while a friend grant is reachable only by the specific named function or class. Using friend sparingly and only where the coupling is already conceptually tight (like an operator or a paired helper class) keeps that exposure intentional rather than accidental.
20. Friend Function vs Member Function — Comparison
Both can access a class's private and protected members, but they differ in call syntax, implicit access, and where conversions can apply.
Aspect | Member function | Friend function
Implicit this / object | Yes, one implicit object | No — every object is a parameter
Call syntax | obj.function(args) | function(obj, args)
Access to private/protected | Yes, on any object of type | Yes, granted explicitly by the class
Implicit conversion on left | Not on the implicit object | Allowed, like any parameter
operand of a binary operator | |
Belongs to class's interface | Yes — listed in the class | No — an ordinary free function with
| | a one-time access grant21. When Friendship Is Appropriate
Friendship reads well when the friend is conceptually inseparable from the class's own behavior — an operator overload like operator<< or a symmetric operator+, a small companion class that manages the object's internal state cooperatively (such as an iterator type tightly bound to its container), or a narrowly scoped testing helper. In each case the coupling already exists in the design; friend just lets the code express it directly instead of routing through an artificially widened public interface.
22. Alternatives to Friendship
Before adding a friend declaration, check whether the same result is reachable through the ordinary public interface: a public accessor function that returns the needed value (or a const reference to it), a member function that performs the operation itself instead of exposing the data it needs, or restructuring the two classes so the operation naturally belongs to one of them as a member. Reserve friend for the cases in section 21, where none of these alternatives express the coupling as directly.
23. Best Practices
1. Grant friendship to the smallest thing that needs it — a single function or a single named member function, rather than a whole class, when only one operation actually requires the access.
2. Prefer a free (friend) function specifically for binary operators that must support implicit conversion on the left-hand operand, such as operator+ or operator==.
3. Write operator<< and operator>> as free functions, declared friend only if they need private data the class doesn't already expose through a public accessor.
4. Keep friend declarations grouped in one predictable place in the class body (commonly near the top) so a reader can see the class's full set of access grants at a glance.
5. Treat a friend function's parameters the same as a member function's implicit object: take by const reference when the friend only reads the object, and by non-const reference only when it must modify it.
6. Reach for friend only after checking whether a public accessor or an ordinary member function already solves the problem — see section 22.
24. Complete Example
operator+ and operator<< are free functions with a friend grant so they can reach x and y directly. VectorInspector is a friend class, so every one of its member functions — here just magnitudeSquared — can also reach x and y directly, without Vector2D exposing them publicly.
#include <iostream>
class Vector2D
{
public:
Vector2D(double xIn, double yIn) : x(xIn), y(yIn) {}
// symmetric operator+ as a friend function
friend Vector2D operator+(const Vector2D& lhs, const Vector2D& rhs);
// stream output as a friend function
friend std::ostream& operator<<(std::ostream& os, const Vector2D& v);
// a small companion class gets full access
friend class VectorInspector;
private:
double x;
double y;
};
Vector2D operator+(const Vector2D& lhs, const Vector2D& rhs)
{
return Vector2D(lhs.x + rhs.x, lhs.y + rhs.y);
}
std::ostream& operator<<(std::ostream& os, const Vector2D& v)
{
os << '(' << v.x << ", " << v.y << ')';
return os;
}
class VectorInspector
{
public:
double magnitudeSquared(const Vector2D& v) const
{
return v.x * v.x + v.y * v.y;
}
};
int main()
{
Vector2D a(1.0, 2.0);
Vector2D b(3.0, 4.0);
Vector2D sum = a + b;
std::cout << "Sum: " << sum << '\n';
VectorInspector inspector;
std::cout << "Magnitude squared of a: " << inspector.magnitudeSquared(a) << '\n';
return 0;
}Output:
Sum: (4, 6)
Magnitude squared of a: 525. Mental Model
Picture a class as a room with one door (its public interface). Friendship hands a specific, named visitor — one function or one class — a key that lets it walk in through the wall directly, bypassing the door, while everyone else still has to use it.
+-----------------------------+
| class A |
| private: secret data |
| |
door --> public interface <-- everyone else uses this
| |
key --> friend function / class <-- direct access, granted by A
+-----------------------------+
The key is cut by A itself (friend declaration inside A).
Holding a key to A does not grant a key to any other class,
and it is not copied to A's derived classes or to A's other friends.26. Quick Reference
Declaration | Meaning
friend void f(const T&); | f() may access T's private/protected members
friend class U; | Every member function of U may access T's private/protected members
friend void U::method(T&); | Only U::method may access T's private/protected members
friend std::ostream& operator<<(...); | Common pattern for printable classes
friend T operator+(const T&, const T&); | Common pattern for symmetric binary operators
Properties: not symmetric, not transitive, not inherited, granted only by the class itself.Key Takeaways
A friend declaration lets a class grant a named function or class direct access to its private and protected members; the grant always comes from the class being accessed, never from the accessing code.
A friend function is an ordinary free function with no implicit this and no implicit object — every object it touches is passed to it as a parameter.
Friendship is not symmetric, not transitive, and not inherited by derived classes; each grant applies only between the exact names it names.
Where a friend declaration appears in the class body (public, private, protected) has no effect on its meaning.
The clearest, most common use of friend is operator overloading — especially operator<< and symmetric operators like operator+ — where the operator naturally has to be a free function.
Prefer the narrowest grant that solves the problem — a single friend function or a single friend member function — over a friend class, and prefer a public accessor over friendship whenever one already does the job.
Why does a friend function need every object it works with passed to it as a parameter, instead of accessing one implicitly the way a member function does?
A friend function is an ordinary free function, not a member of the class — it has no implicit this pointer and no implicit object bound to the call. A member function can write member (meaning this->member) because the compiler supplies this automatically; a friend function has no such implicit object, so every object whose members it needs must be passed in explicitly as a parameter.
Class A declares "friend class B;" inside itself. Does this mean B must also declare "friend class A;" for A to access B's private members?
Yes. Friendship is not symmetric — A declaring B a friend only grants B's member functions access into A. It does not grant A any access into B. If A also needs access to B's private members, B must separately write its own "friend class A;" declaration.
Why is operator+ for a class often written as a free (friend) function instead of a member function, when the class also supports implicit construction from another type (e.g. from int)?
A member operator+ can only apply an implicit conversion to its right-hand operand, because the left-hand operand must already be the class type — it is the implicit object the member is called on. A free function operator+ has no implicit object, so the compiler can apply an implicit conversion to either operand, letting expressions like "25 + Money(150)" compile as well as "Money(150) + 25".
If class Base declares "friend class Logger;", does a class Derived that publicly inherits from Base automatically treat Logger as a friend too?
No. Friendship is not inherited. Logger's friendship applies to Base's own members; Derived does not automatically extend that grant to the members it adds, and Derived must declare its own friend if it wants the same access.
What is the difference in scope between "friend class Auditor;" written inside class Ledger, and "friend void Auditor::checkBalance(const Ledger&);" written inside class Ledger?
"friend class Auditor;" grants every member function of Auditor access to Ledger's private and protected members. "friend void Auditor::checkBalance(const Ledger&);" grants access to only that one named member function of Auditor — Auditor's other member functions get no special access to Ledger.
Write a class Rectangle with private members width and height (both int) and a constructor that sets them. Then write a free function "area" that takes a const Rectangle& and returns its area (width * height), declaring it a friend of Rectangle so it can access the private members directly.
Prints 20. Rectangle grants "area" friend access, so the free function can read r.width and r.height directly even though both are private; area is not a member of Rectangle, so it is called as area(r), not r.area().
What does this program print, and why is Resetter allowed to access c.value directly in both of its member functions?
It prints 1. bump is called three times (value goes 0→1→2→3), then reset sets value back to 0, then bump is called once more (value becomes 1). Resetter can access Counter's private value in both reset and bump because Counter declared "friend class Resetter;" — that grants access to every member function of Resetter, not just one.
Write a class Fraction with private int members numerator and denominator, a two-argument constructor, and a friend operator<< that prints it in the form "numerator/denominator" to an ostream.
Prints "1/2". operator<< must be a free function because its left operand is std::ostream (std::cout), not Fraction, so it cannot be a Fraction member; it is declared friend so it can read Fraction's private numerator and denominator directly, and it returns the ostream& so output can be chained.
Assuming Gamma is otherwise unrelated to Alpha and Beta (no friend declarations mention Gamma), which of the two marked lines, (1) or (2), fails to compile, and why?
Line (1), Gamma::readAlpha, fails to compile — Alpha only declared "friend class Beta;", so only Beta's member functions may access Alpha's private members; Gamma has no such grant. Line (2) also fails, actually, for the same reason with respect to Beta::b — Beta grants no friendship to Gamma either. Neither Gamma method compiles: friendship is granted only to the exact class named, and Gamma was never named by either class.
class Counter
{
public:
Counter() : value(0) {}
friend class Resetter;
private:
int value = 0;
};
class Resetter
{
public:
void reset(Counter& c) const
{
c.value = 0;
}
void bump(Counter& c) const
{
c.value += 1;
}
};
int main()
{
Counter c;
Resetter r;
r.bump(c);
r.bump(c);
r.bump(c);
r.reset(c);
r.bump(c);
std::cout << c.value << '\n';
return 0;
}class Alpha
{
public:
friend class Beta;
private:
int a = 10;
};
class Beta
{
public:
int readAlpha(const Alpha& x) const { return x.a; }
private:
int b = 20;
};
class Gamma
{
public:
int readAlpha(const Alpha& x) const { return x.a; } // (1)
int readBeta(const Beta& y) const { return y.b; } // (2)
};