Docs
README
Friend Functions, Friend Classes & Functors
Overview
Friend functions and friend classes provide a way to grant special access privileges to external code, while functors (function objects) enable objects to be called like functions, providing powerful flexibility in C++.
Learning Objectives
By the end of this section, you will understand:
- •Friend functions and when to use them
- •Friend classes for tight coupling
- •Why friendship is not inherited
- •Functors and how they work
- •Comparison with lambda expressions
- •Use cases in STL algorithms
Friend Functions
A friend function is a non-member function that has access to private and protected members of a class.
┌─────────────────────────────────────────────────────────────────┐
│ FRIEND FUNCTION │
├─────────────────────────────────────────────────────────────────┤
│ │
│ class Box { │
│ private: │
│ int width; ◄──── Private member │
│ │
│ public: │
│ friend void show(Box&); ◄── Grants access to show() │
│ }; │
│ │
│ void show(Box& b) { ◄──── NOT a member function! │
│ cout << b.width; ◄──── Can access private member │
│ } │
│ │
└─────────────────────────────────────────────────────────────────┘
Syntax
class MyClass {
private:
int secret;
public:
// Declare friend function
friend void accessSecret(MyClass& obj);
friend int getSecret(const MyClass& obj);
};
// Define friend function (NOT a member!)
void accessSecret(MyClass& obj) {
obj.secret = 42; // Can access private member
}
When to Use Friend Functions
┌───────────────────────────────────────────────────────────────────────────┐
│ FRIEND FUNCTION USE CASES │
├───────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. OPERATOR OVERLOADING (with left operand not being the class) │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ friend ostream& operator<<(ostream& os, const Obj& o); │ │
│ │ // cout << obj; (cout is left operand) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ 2. TWO-CLASS OPERATIONS │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ friend bool compare(const ClassA& a, const ClassB& b); │ │
│ │ // Needs access to both classes │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ 3. FACTORY FUNCTIONS │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ friend MyClass createOptimized(int val); │ │
│ │ // Needs to set private members directly │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────────┘
Friend Classes
A friend class can access all private and protected members of the class that declares it as friend.
┌─────────────────────────────────────────────────────────────────────────┐
│ FRIEND CLASS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────┐ ┌───────────────────────┐ │
│ │ Engine │ │ Car │ │
│ ├───────────────────────┤ ├───────────────────────┤ │
│ │ - rpm: int │◄───────│ friend class Engine; │ │
│ │ - temp: float │ ACCESS │ - speed: int │ │
│ ├───────────────────────┤ │ - fuel: float │ │
│ │ + tune(Car&) │────────► │ │
│ │ + diagnose(Car&) │ │ │ │
│ └───────────────────────┘ └───────────────────────┘ │
│ │
│ Engine can access ALL private members of Car │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Syntax
class Car {
private:
int speed;
float fuel;
public:
friend class Engine; // Engine can access private members
};
class Engine {
public:
void tune(Car& car) {
car.speed = 0; // Can access private
car.fuel = 100.0f; // Can access private
}
};
Friendship Properties
┌─────────────────────────────────────────────────────────────────────────┐
│ IMPORTANT FRIENDSHIP PROPERTIES │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. NOT SYMMETRIC │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ class A { friend class B; }; │ │
│ │ // B can access A's private members │ │
│ │ // A CANNOT access B's private members │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 2. NOT TRANSITIVE │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ class A { friend class B; }; │ │
│ │ class B { friend class C; }; │ │
│ │ // C CANNOT access A's private members │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 3. NOT INHERITED │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ class A { friend class B; }; │ │
│ │ class C : public B { }; │ │
│ │ // C is NOT a friend of A │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Functors (Function Objects)
A functor is an object that can be called like a function by overloading operator().
┌─────────────────────────────────────────────────────────────────────────┐
│ FUNCTOR ANATOMY │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ class Multiplier { │
│ int factor; ◄── STATE (can vary) │
│ │
│ public: │
│ Multiplier(int f) : factor(f) {} │
│ │
│ int operator()(int x) const { ◄── Makes it callable │
│ return x * factor; │
│ } │
│ }; │
│ │
│ Multiplier times5(5); ◄── Create functor │
│ int result = times5(10); ◄── Call like function │
│ └── returns 50 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Functor vs Regular Function
┌─────────────────────────────────────────────────────────────────────────┐
│ FUNCTOR vs FUNCTION COMPARISON │
├───────────────────────────────┬─────────────────────────────────────────┤
│ FUNCTION │ FUNCTOR │
├───────────────────────────────┼─────────────────────────────────────────┤
│ │ │
│ int multiply(int x) { │ class Multiplier { │
│ return x * 5; │ int factor; │
│ } │ public: │
│ │ Multiplier(int f): factor(f) {} │
│ │ int operator()(int x) { │
│ // Fixed behavior │ return x * factor; │
│ │ } │
│ │ }; │
│ │ │
│ multiply(10); // Always 50 │ Multiplier m(5); │
│ │ m(10); // 50 │
│ │ Multiplier n(3); │
│ │ n(10); // 30 - Different behavior! │
│ │ │
├───────────────────────────────┴─────────────────────────────────────────┤
│ Functors can have STATE and CONFIGURABLE BEHAVIOR │
└─────────────────────────────────────────────────────────────────────────┘
STL Functors
┌─────────────────────────────────────────────────────────────────────────┐
│ STL PREDEFINED FUNCTORS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ #include <functional> │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ ARITHMETIC FUNCTORS │ │
│ │ ───────────────────── │ │
│ │ std::plus<T> │ a + b │ │
│ │ std::minus<T> │ a - b │ │
│ │ std::multiplies<T> │ a * b │ │
│ │ std::divides<T> │ a / b │ │
│ │ std::modulus<T> │ a % b │ │
│ │ std::negate<T> │ -a │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ COMPARISON FUNCTORS │ │
│ │ ─────────────────── │ │
│ │ std::equal_to<T> │ a == b │ │
│ │ std::not_equal_to<T> │ a != b │ │
│ │ std::greater<T> │ a > b │ │
│ │ std::less<T> │ a < b │ │
│ │ std::greater_equal<T> │ a >= b │ │
│ │ std::less_equal<T> │ a <= b │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ LOGICAL FUNCTORS │ │
│ │ ──────────────── │ │
│ │ std::logical_and<T> │ a && b │ │
│ │ std::logical_or<T> │ a || b │ │
│ │ std::logical_not<T> │ !a │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Functors with STL Algorithms
┌─────────────────────────────────────────────────────────────────────────┐
│ FUNCTORS WITH STL ALGORITHMS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ std::vector<int> v = {5, 2, 8, 1, 9}; │
│ │
│ // Sort descending using std::greater │
│ std::sort(v.begin(), v.end(), std::greater<int>()); │
│ // v = {9, 8, 5, 2, 1} │
│ │
│ // Custom predicate functor │
│ struct IsEven { │
│ bool operator()(int n) const { │
│ return n % 2 == 0; │
│ } │
│ }; │
│ │
│ // Count even numbers │
│ int count = std::count_if(v.begin(), v.end(), IsEven()); │
│ │
│ // Accumulate with multiplies │
│ int product = std::accumulate(v.begin(), v.end(), 1, │
│ std::multiplies<int>()); │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Functors vs Lambdas
┌─────────────────────────────────────────────────────────────────────────┐
│ FUNCTORS vs LAMBDA EXPRESSIONS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ FUNCTOR: │
│ ──────── │
│ struct GreaterThan { │
│ int threshold; │
│ GreaterThan(int t) : threshold(t) {} │
│ bool operator()(int x) const { │
│ return x > threshold; │
│ } │
│ }; │
│ │
│ auto it = std::find_if(v.begin(), v.end(), GreaterThan(5)); │
│ │
│ ═══════════════════════════════════════════════════════════════════ │
│ │
│ LAMBDA (equivalent): │
│ ──────────────────── │
│ int threshold = 5; │
│ auto it = std::find_if(v.begin(), v.end(), │
│ [threshold](int x) { return x > threshold; }); │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────┬────────────────────────────────────────────┐ │
│ │ FUNCTOR │ LAMBDA │ │
│ ├────────────────────┼────────────────────────────────────────────┤ │
│ │ Reusable │ Usually single-use │ │
│ │ Named │ Anonymous │ │
│ │ More verbose │ Concise │ │
│ │ Can inherit │ Cannot inherit │ │
│ │ Multiple operators │ Single operator() │ │
│ └────────────────────┴────────────────────────────────────────────┘ │
│ │
│ Tip: Lambdas are actually anonymous functor classes! │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Common Patterns
Pattern 1: Stream Operators as Friends
class Point {
int x, y;
public:
Point(int x, int y) : x(x), y(y) {}
// Friend for output stream
friend std::ostream& operator<<(std::ostream& os, const Point& p) {
return os << "(" << p.x << ", " << p.y << ")";
}
// Friend for input stream
friend std::istream& operator>>(std::istream& is, Point& p) {
return is >> p.x >> p.y;
}
};
Pattern 2: Builder Pattern with Friend
class Product {
std::string name;
float price;
Product() = default;
friend class ProductBuilder; // Only builder can create
public:
void display() const { /* ... */ }
};
class ProductBuilder {
Product product;
public:
ProductBuilder& setName(const std::string& n) {
product.name = n;
return *this;
}
ProductBuilder& setPrice(float p) {
product.price = p;
return *this;
}
Product build() { return product; }
};
Pattern 3: Stateful Functor
class RunningAverage {
double sum = 0;
int count = 0;
public:
void operator()(double value) {
sum += value;
++count;
}
double getAverage() const {
return count > 0 ? sum / count : 0;
}
};
// Usage
RunningAverage avg;
avg(10); avg(20); avg(30);
std::cout << avg.getAverage(); // 20
Best Practices
┌─────────────────────────────────────────────────────────────────────────┐
│ BEST PRACTICES │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ FRIENDS: │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ ✓ Use sparingly - breaks encapsulation │ │
│ │ ✓ Good for operator overloading (<<, >>, +, etc.) │ │
│ │ ✓ Good for factory functions │ │
│ │ ✗ Avoid making entire classes friends if only one function needs │ │
│ │ ✗ Don't use to work around bad design │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
│ FUNCTORS: │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ ✓ Make operator() const when possible │ │
│ │ ✓ Use for reusable predicates/transformers │ │
│ │ ✓ Prefer lambdas for simple, one-off uses │ │
│ │ ✓ Use STL functors when applicable │ │
│ │ ✓ Functors can be more performant (inline potential) │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Summary
| Concept | Purpose | Key Points |
|---|---|---|
| Friend Function | Access private members | Not a member, breaks encapsulation selectively |
| Friend Class | Full access to another class | Not symmetric, not transitive, not inherited |
| Functor | Callable object | Has state, used with STL algorithms |
| std::function | Type-erased callable | Stores functors, lambdas, or function pointers |
Next Steps
- •Practice with
examples.cpp - •Complete the
exercises.cppchallenges - •Explore STL algorithms that use functors
- •Learn about
std::functionandstd::bind