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

ConceptPurposeKey Points
Friend FunctionAccess private membersNot a member, breaks encapsulation selectively
Friend ClassFull access to another classNot symmetric, not transitive, not inherited
FunctorCallable objectHas state, used with STL algorithms
std::functionType-erased callableStores functors, lambdas, or function pointers

Next Steps

  • Practice with examples.cpp
  • Complete the exercises.cpp challenges
  • Explore STL algorithms that use functors
  • Learn about std::function and std::bind
README - C++ Tutorial | DeepML