Welcome! If you’re learning C++ and want a crystal-clear explanation of Friend Functions in C++, this guide is for you. We’ll use plain language, step-by-step examples, and code with comments so you always understand what’s happening.
Think of Friend Functions in C++ as trusted helpers that aren’t members of a class but still get permission to access its private/protected parts—only when you explicitly allow it. After each section you’ll find “Try it yourself” challenges to practice.
🔹 What are Friend Functions in C++?
A friend function is a non-member function that you grant access to a class’s private and protected members using the friend
keyword inside the class. Use it when an operation feels “external” to the class but still needs deep access—like pretty-printing with operator<<
, equality checks, or arithmetic on two objects.
Quick facts:
- Friendship is granted by the class (not inherited or automatic).
- Friendship is not transitive (a friend of my friend is not my friend).
- Friendship is not reciprocal (if A befriends B, B doesn’t automatically befriend A).
- Friend functions are free functions (not members) with special access.
Try it yourself
- In your own words, explain why you might prefer a friend function over adding another public getter.
- Sketch a class
Safe
with a privatecode
and a non-memberopen(Safe&)
function that needs friend access.
🔹 Basic Syntax and First Example
Declare the friend function inside the class, then define it like a normal free function (usually outside the class).
#include <iostream>
using namespace std;
class Box {
private:
double w, h, d; // private dimensions
public:
Box(double w_, double h_, double d_) : w(w_), h(h_), d(d_) {}
// Grant friend access to a non-member function
friend double volume(const Box& b);
};
// Defined outside the class: still has access due to friendship
double volume(const Box& b)
{ return b.w * b.h * b.d; }
int main() {
Box b(2.0, 3.0, 5.0);
cout << "Volume: " <<
volume(b) << "\n";
return 0;
}
Output
Volume: 30
Why friend here? volume
is a natural free function—it operates on a Box
but doesn’t conceptually “belong” to a specific box instance as a method. We also pass by const&
to avoid copying.
Try it yourself
- Add a second friend function
surfaceArea(const Box&)
that returns2*(w*h + w*d + h*d)
and print it. - Validate the constructor: if a dimension ≤ 0, print a warning.
- Write a friend
bool isBigger(const Box& a, const Box& b)
that compares volumes.
🔹 When to Use Friend Functions (and When Not To)
Use Friend Functions in C++ when:
- An operation is symmetric and doesn’t fit as a member (e.g.,
a + b
,a == b
). - The left operand isn’t your type (e.g.,
std::ostream << obj
). - You want to keep the class API small but allow specific helpers deep access.
Avoid or reconsider friends when:
- A narrow public API (getters/setters) is sufficient.
- Friendship would break encapsulation without strong justification.
- The logic belongs inside the class as a member function.
Try it yourself
- Refactor one of your member functions into a free function. Does it still need to be a friend?
- List two operations on your class that feel symmetric and decide friend vs member.
🔹 Operator Overloading with Friend Functions
Many operators are best as non-members. For example, printing with operator<<
and arithmetic like operator+
are often implemented as friend functions.
#include <iostream>
using namespace std;
class Vector2D {
private:
double x{0}, y{0};
public:
Vector2D() = default;
Vector2D(double x_, double y_) : x(x_), y(y_) {}
// Stream output: lhs is std::ostream (not our class), so make it a friend
friend ostream& operator<<
(ostream& os, const Vector2D& v);
// Symmetric addition: non-member friend
friend Vector2D operator+
(const Vector2D& a, const Vector2D& b);
};
ostream& operator<<(ostream& os,
const Vector2D& v) {
os << "Vector2D(" << v.x
<< ", " << v.y << ")";
return os;
}
Vector2D operator+(const Vector2D& a,
const Vector2D& b) {
return Vector2D(a.x + b.x, a.y + b.y); // access private members
}
int main() {
Vector2D v1(2, 3), v2(4, 5);
cout << v1 << " + " << v2
<< " = " << (v1 + v2) << "\n";
}
Output
Vector2D(2, 3) + Vector2D(4, 5) = Vector2D(6, 8)
Why friend? operator<<
must take std::ostream&
first, and operator+
is symmetric. Both benefit from direct private access.
Try it yourself
- Add
friend bool operator==(const Vector2D&, const Vector2D&)
. - Write a non-friend
double magnitude(const Vector2D&)
using getters only (add getters if needed). - Overload
operator-
as a friend.
🔹 One Friend Function for Multiple Classes
A single free function can be a friend of multiple classes when it needs to access private data from each.
#include <iostream>
using namespace std;
class SecretA;
class SecretB;
// Forward declare the free function so classes can friend it
void swapSecrets(SecretA& a, SecretB& b);
class SecretA {
private:
int codeA{42};
public:
friend void swapSecrets(SecretA&, SecretB&);
int get() const { return codeA; }
};
class SecretB {
private:
int codeB{7};
public:
friend void swapSecrets(SecretA&, SecretB&);
int get() const { return codeB; }
};
void swapSecrets(SecretA& a, SecretB& b) {
int tmp = a.codeA;
a.codeA = b.codeB;
b.codeB = tmp;
}
int main() {
SecretA a;
SecretB b;
cout << "Before: A=" << a.get()
<< ", B=" << b.get() << "\n";
swapSecrets(a, b);
cout << "After: A=" << a.get()
<< ", B=" << b.get() << "\n";
}
Output
Before: A=42, B=7
After: A=7, B=42
Try it yourself
- Add
SecretC
and implementrotateSecrets(a, b, c)
that rotates values across all three. - Try implementing swap using only getters—notice how it’s harder without friend access.
🔹 Const Correctness and Parameters
Friend functions follow the same best practices as any free function: pass by const&
for read-only, and by non-const reference when you need to modify the object.
#include <iostream>
#include <string>
using namespace std;
class Account {
private:
string owner;
double balance{0.0};
public:
Account(string name, double amt) :
owner(std::move(name)), balance(amt) {}
friend double getBalance(const Account& a); // read-only
friend bool deposit(Account& a, double amt); // mutates
friend bool withdraw(Account& a, double amt); // mutates
};
double getBalance(const Account& a) { return a.balance; }
bool deposit(Account& a, double amt) {
if (amt <= 0) return false;
a.balance += amt;
return true;
}
bool withdraw(Account& a, double amt) {
if (amt <= 0 || amt > a.balance)
return false;
a.balance -= amt;
return true;
}
int main() {
Account acc("Sam", 100.0);
deposit(acc, 50.0);
cout << "After deposit: "
<< getBalance(acc) << "\n";
withdraw(acc, 120.0);
cout << "After withdraw: "
<< getBalance(acc) << "\n";
}
Output
After deposit: 150
After withdraw: 30
Try it yourself
- Add
friend const string& ownerName(const Account&)
. - Make
withdraw
return false if the account would go negative; test edge cases.
🔹 Templates and Friend Functions
You can befriend function templates and/or specific overloads. This is especially useful in generic code.
#include <iostream>
#include <string>
using namespace std;
template <typename T>
class Box {
private:
T value;
public:
explicit Box(T v) : value(std::move(v)) {}
// Befriend a function template
template <typename U>
friend U getValue(const Box<U>& b);
// Befriend a specific non-template function (only for Box<int>)
friend void printIntBox(const Box<int>& b);
};
template <typename U>
U getValue(const Box<U>& b) { return b.value; }
void printIntBox(const Box<int>& b)
{ cout << "Int box: " << b.value << "\n"; }
int main() {
Box<int> bi(10);
Box<string> bs("hello");
cout << getValue(bi) << "\n";
cout << getValue(bs) << "\n";
printIntBox(bi);
// printIntBox(bs); // won't compile: only Box<int> is friended
}
Output
10
hello
Int box: 10
Try it yourself
- Add a friend template
sum(const Box<T>&, const Box<T>&)
that returnsT
. - Befriend only a specific function like
debug(const Box<double>&)
and verify others lack access.
🔹 Friends, Inheritance, and Access
Friendship and inheritance are separate. A friend of a base class can access the base subobject inside derived objects, but it’s not automatically a friend of the derived class itself.
#include <iostream>
using namespace std;
class Base {
private:
int secret{123};
protected:
int semi{456};
public:
friend void peek(const Base&);
};
class Derived : public Base { };
void peek(const Base& b) {
cout << "secret=" << b.secret
<< ", semi=" << b.semi << "\n";
}
int main() {
Derived d;
peek(d); // accesses Base part of Derived
}
Output
secret=123, semi=456
Try it yourself
- Add a private member to
Derived
and try to access it frompeek
—see the error. - Add friendship specifically in
Derived
to a new functionpeekDerived
.
🔹 Best Practices and Common Pitfalls
- Document the “why” above each
friend
declaration. - Prefer friend functions for symmetric operations and formatting.
- Avoid unnecessary friends—use narrow public APIs when possible.
- No virtual friends (only members can be virtual).
- Use forward declarations to avoid include cycles when declaring friends.
- Respect const correctness in friend functions.
Try it yourself
- Remove one unnecessary friend by introducing a small const getter.
- Add a comment to each remaining friend explaining its purpose in one sentence.
🔹 Quick Reference: Friend Function Patterns
// Friend free function (read-only)
class A { int x; public: friend int inspect(const A& a); };
// Friend free function (mutating)
class B { int x; public: friend void tweak(B& b); };
// operator<< as friend
class C { int x; public: friend std::ostream& operator<<
(std::ostream&, const C&); };
// Friend function template
template <typename T>
class E { T v; template <typename U>
friend U get(const E<U>&); };
🔹 FAQs about Friend Functions in C++
Q1: Do friend functions break encapsulation?
They can if overused. Use them deliberately for well-justified cases (formatters, equality, symmetric operators). Try it: replace one friend with a tiny getter + non-friend helper.
Q2: Are friends inherited or transitive?
No. A friend of a base isn’t a friend of derived, and a friend of a friend is not a friend. Try it: add a derived class and observe compile errors without explicit friendship.
Q3: Can a friend function be virtual?
No—friends are not members. If you need polymorphism, use virtual members. Try it: attempt “virtual friend” and fix the error.
Q4: Do friend functions affect performance?
No inherent runtime cost—they only change access permissions at compile time. Try it: benchmark friend vs getter-based implementations.
Q5: Can I friend only one overload or specialization?
Yes. You can friend a specific function or a function template. Try it: friend only print(const Box<int>&)
and confirm the string version can’t access internals.
🔹 Summary
Friend Functions in C++ provide precise access for non-member helpers that need to work closely with a class’s internals. Use them thoughtfully for symmetric operators, formatting, and specific helpers. Keep APIs small, document intent, and practice with the challenges above. Happy coding!