C++ Friend Functions
In C++, a class’s private and protected members are normally hidden from everything outside the class. A friend function is a special exception to that rule: it is a function (or another class) that you explicitly grant permission to reach inside your class and touch its private data, even though it is not itself a member. Friend functions exist because sometimes the most natural way to express an operation — comparing two objects, printing an object, combining two unrelated classes — is as a free function rather than a method, but that function still needs privileged access to do its job.
Overview / How Friend Functions Work
Normally, encapsulation means only member functions of a class (and its own methods) can see the class’s private and protected members. A friend declaration punches a deliberate, controlled hole in that wall. You declare a function as a friend inside the class body, but the function itself is defined outside the class, exactly like an ordinary free function — it is not a member, has no this pointer, and is not called with the dot or arrow operator.
Under the hood, granting friendship changes nothing about how the object is laid out in memory. It is purely a compile-time access-control relaxation: the compiler simply allows the friend function’s code to reference the private members directly instead of rejecting it. There is no runtime cost, no vtable entry, and no indirection — friendship is resolved entirely during compilation, the same way public/private access is checked.
Three things are important to remember about friendship:
- Friendship is granted, not taken. Only the class itself can declare who its friends are; a function cannot declare itself a friend of some class.
- Friendship is not symmetric. If class
Adeclares classBa friend,Bcan accessA‘s private members, butAcannot accessB‘s private members unlessBseparately grants that. - Friendship is not inherited and not transitive. A friend of a base class is not automatically a friend of a derived class, and a friend of your friend is not your friend.
You can make three kinds of things friends: a global (free) function, a member function of another class, or an entire class (in which case every member function of that class becomes a friend).
Syntax
class ClassName {
private:
// hidden data
// grant friendship to a free function
friend returnType functionName(parameters);
// grant friendship to a member of another class
friend returnType OtherClass::memberFunction(parameters);
// grant friendship to an entire class
friend class OtherClass;
};
- friend — the keyword that appears inside the class definition; it can be placed under any access section (public, private, or protected) since it does not affect the friend’s own access level, only who can see the granting class’s internals.
- function signature — the friend function is declared with its full signature so the compiler knows exactly which function (or overload) is being trusted.
- friend class OtherClass; — makes every method of
OtherClassable to access the granting class’s private and protected members. - The friend function/class is defined normally elsewhere — the
friendkeyword never appears again outside the class body.
Examples
Example 1: A basic friend function
#include <iostream>
using namespace std;
class Box {
private:
double width;
double height;
public:
Box(double w, double h) : width(w), height(h) {}
friend double calculateArea(const Box& b);
};
double calculateArea(const Box& b) {
return b.width * b.height;
}
int main() {
Box b(4.5, 3.0);
cout << "Area: " << calculateArea(b) << endl;
return 0;
}
Output:
Area: 13.5
calculateArea is declared inside Box with the friend keyword, but it is defined and called exactly like a normal free function — note the call is calculateArea(b), not b.calculateArea(). Because it is a friend, it can read b.width and b.height directly even though they are private.
Example 2: Friend functions for operator overloading
The most common real-world use of friend functions is overloading operators like << for printing, where the left-hand operand must be the stream rather than the class object, so the operator cannot be a member function of the class.
#include <iostream>
using namespace std;
class Point {
private:
int x, y;
public:
Point(int xVal, int yVal) : x(xVal), y(yVal) {}
friend ostream& operator<<(ostream& out, const Point& p);
friend bool operator==(const Point& lhs, const Point& rhs);
};
ostream& operator<<(ostream& out, const Point& p) {
out << "(" << p.x << ", " << p.y << ")";
return out;
}
bool operator==(const Point& lhs, const Point& rhs) {
return lhs.x == rhs.x && lhs.y == rhs.y;
}
int main() {
Point p1(3, 4);
Point p2(3, 4);
Point p3(5, 6);
cout << "p1 = " << p1 << endl;
cout << "p2 = " << p2 << endl;
cout << "p3 = " << p3 << endl;
if (p1 == p2)
cout << "p1 and p2 are equal" << endl;
else
cout << "p1 and p2 are not equal" << endl;
if (p1 == p3)
cout << "p1 and p3 are equal" << endl;
else
cout << "p1 and p3 are not equal" << endl;
return 0;
}
Output:
p1 = (3, 4)
p2 = (3, 4)
p3 = (5, 6)
p1 and p2 are equal
p1 and p3 are not equal
Both operator<< and operator== need direct access to the private x and y members but cannot be ordinary member functions (the stream must come first for <<, and a free function reads more naturally for symmetric comparisons). Declaring them as friends solves both problems at zero runtime cost.
Example 3: A friend function shared by two different classes
#include <iostream>
using namespace std;
class Fahrenheit;
class Celsius {
private:
double temp;
public:
Celsius(double t) : temp(t) {}
friend void compareTemps(const Celsius& c, const Fahrenheit& f);
};
class Fahrenheit {
private:
double temp;
public:
Fahrenheit(double t) : temp(t) {}
friend void compareTemps(const Celsius& c, const Fahrenheit& f);
};
void compareTemps(const Celsius& c, const Fahrenheit& f) {
double cInF = c.temp * 9.0 / 5.0 + 32.0;
cout << "Celsius " << c.temp << " C = " << cInF << " F" << endl;
cout << "Fahrenheit value stored: " << f.temp << " F" << endl;
if (cInF > f.temp)
cout << "The Celsius temperature is hotter." << endl;
else if (cInF < f.temp)
cout << "The Fahrenheit temperature is hotter." << endl;
else
cout << "Both temperatures are equal." << endl;
}
int main() {
Celsius c(100);
Fahrenheit f(200);
compareTemps(c, f);
return 0;
}
Output:
Celsius 100 C = 212 F
Fahrenheit value stored: 200 F
The Celsius temperature is hotter.
Here, compareTemps needs to read private data belonging to two unrelated classes. A forward declaration (class Fahrenheit;) is required so that Celsius can mention Fahrenheit in its friend declaration before Fahrenheit is fully defined. Both classes list the same function as a friend, giving that one function joint access to both sets of private data — something no single member function could do.
Example 4: A friend class
#include <iostream>
using namespace std;
class Engine {
private:
int horsepower;
public:
Engine(int hp) : horsepower(hp) {}
friend class Car;
};
class Car {
public:
void showEngineDetails(const Engine& e) {
cout << "Engine horsepower: " << e.horsepower << endl;
}
};
int main() {
Engine e(450);
Car c;
c.showEngineDetails(e);
return 0;
}
Output:
Engine horsepower: 450
friend class Car; inside Engine makes every member function of Car able to access Engine‘s private data, not just one function. This is convenient when two classes are tightly coupled by design (like a Car that legitimately needs to inspect its own Engine‘s internals), but it is a broad grant — use it only when the two classes truly form one conceptual unit.
How It Works Step by Step
- The compiler parses the class definition and records, alongside the member list, which external functions or classes have been declared as friends.
- When it later compiles the body of a friend function, it checks whether the class being accessed lists that function (or the function’s enclosing class) as a friend.
- If it is listed, private/protected member access is allowed exactly as if it were a member function; if not, the compiler raises an access error.
- Friendship is checked purely by name and signature at compile time — there is no object stored, no flag set at runtime, and no extra memory used per object.
- Because friendship is a compile-time relationship between named entities, it cannot be granted or revoked while the program runs, and it does not propagate through inheritance or through other friendships.
Common Mistakes
Mistake 1: Calling a friend function with member syntax
A friend function is not a member, so it can never be called through an object with the dot operator.
class Box {
private:
double width;
public:
Box(double w) : width(w) {}
friend double getWidth(const Box& b);
};
double getWidth(const Box& b) {
return b.width;
}
int main() {
Box b(5.0);
cout << b.getWidth() << endl; // ERROR: getWidth is not a member of Box
return 0;
}
This fails to compile because getWidth was never added to Box‘s member list — friendship grants access, not membership. The fix is to call it the way any free function is called:
#include <iostream>
using namespace std;
class Box {
private:
double width;
public:
Box(double w) : width(w) {}
friend double getWidth(const Box& b);
};
double getWidth(const Box& b) {
return b.width;
}
int main() {
Box b(5.0);
cout << getWidth(b) << endl;
return 0;
}
Output:
5
Mistake 2: Assuming friendship is inherited
A very common misconception is that if class B is a friend of class A, then classes derived from B automatically share that friendship too.
class A {
friend class B;
private:
int secret = 42;
};
class B {
public:
void reveal(A& a) {
cout << a.secret << endl; // OK, B is a friend of A
}
};
class C : public B {
public:
void tryReveal(A& a) {
cout << a.secret << endl; // ERROR: C is not a friend of A
}
};
C inherits B‘s public interface but not its friendships — friendship belongs to the specific class named in the friend declaration and is never passed down to subclasses. If C genuinely needs the same access, A must explicitly add friend class C; as well.
Best Practices
- Prefer member functions or public accessor methods first; reach for a friend function only when the operation genuinely cannot be a clean member (e.g.
operator<<, or a function that must access two different classes symmetrically). - Keep the friend function’s job narrow — friendship should grant access for one clear purpose, not become a backdoor for general-purpose meddling with a class’s internals.
- Prefer a single friend function over
friend classwhen only one operation needs access; a whole-class grant exposes far more than most tasks require. - Document why a friend declaration exists, since it breaks the normal encapsulation story and future readers will wonder why.
- Remember friendship is one-directional and non-transitive — grant it explicitly on every class that needs to expose access, and don’t assume it flows through inheritance or between mutual friends.
- Avoid overusing friends as a substitute for good class design; if you find yourself adding many friend declarations, it may be a sign the classes should be merged or redesigned.
Practice Exercises
Exercise 1: Write a class Rectangle with private length and width. Add a friend function bool isSquare(const Rectangle& r) that returns whether the rectangle is a square. Test it with a 5×5 rectangle and a 4×6 rectangle.
Exercise 2: Write a class Vector2D with private x and y. Overload operator+ as a friend function to add two Vector2D objects and return a new one, and overload operator<< as a friend function to print it in the form (x, y).
Exercise 3: Create two classes, Wallet (with a private balance) and Bank. Make Bank a friend class of Wallet and give Bank a member function that can directly inspect and modify a Wallet‘s balance. Explain in a comment why a friend class is appropriate here instead of a friend function.
Summary
- A friend function or friend class is granted explicit access to a class’s private and protected members, even though it is not itself a member.
- Friendship is declared with the
friendkeyword inside the class body, but the friend is defined and called like an ordinary function or class — never with dot/arrow member syntax. - Friendship costs nothing at runtime; it is purely a compile-time access-control decision.
- Friendship is one-directional, not symmetric, not inherited, and not transitive.
- Typical uses include overloading operators like
<<, comparing or combining two different classes, and letting tightly coupled classes share internals. - Use friends sparingly — they intentionally weaken encapsulation, so reserve them for cases where a clean member-function design isn’t possible.
