cpp

examples

examples.cpp⚙️
/**
 * Friend Functions, Friend Classes & Functors - Examples
 * Comprehensive demonstrations of C++ friendship and callable objects
 */

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <numeric>
#include <functional>
#include <map>
#include <iomanip>

using std::cout;
using std::endl;
using std::string;

// ============================================================================
// SECTION 1: Friend Function Basics
// ============================================================================

class Box {
private:
    double width;
    double height;
    double depth;
    
public:
    Box(double w, double h, double d) : width(w), height(h), depth(d) {}
    
    // Declare friend function - can access private members
    friend double calculateVolume(const Box& box);
    friend void printDimensions(const Box& box);
    
    // Friend function for comparing two boxes
    friend bool areSameSize(const Box& a, const Box& b);
};

// Friend function implementations (NOT member functions!)
double calculateVolume(const Box& box) {
    // Can access private members
    return box.width * box.height * box.depth;
}

void printDimensions(const Box& box) {
    cout << "  Box: " << box.width << " x " << box.height << " x " << box.depth << endl;
}

bool areSameSize(const Box& a, const Box& b) {
    return a.width == b.width && 
           a.height == b.height && 
           a.depth == b.depth;
}

void demonstrateFriendFunctions() {
    cout << "\n=== FRIEND FUNCTION BASICS ===" << endl;
    
    Box box1(3.0, 4.0, 5.0);
    Box box2(3.0, 4.0, 5.0);
    Box box3(2.0, 2.0, 2.0);
    
    cout << "\nBox 1:" << endl;
    printDimensions(box1);
    cout << "  Volume: " << calculateVolume(box1) << endl;
    
    cout << "\nBox 3:" << endl;
    printDimensions(box3);
    cout << "  Volume: " << calculateVolume(box3) << endl;
    
    cout << "\nComparisons:" << endl;
    cout << "  Box1 same size as Box2: " << (areSameSize(box1, box2) ? "Yes" : "No") << endl;
    cout << "  Box1 same size as Box3: " << (areSameSize(box1, box3) ? "Yes" : "No") << endl;
}

// ============================================================================
// SECTION 2: Friend Function for Operator Overloading
// ============================================================================

class Complex {
private:
    double real;
    double imag;
    
public:
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}
    
    // Member function for addition (left operand is Complex)
    Complex operator+(const Complex& other) const {
        return Complex(real + other.real, imag + other.imag);
    }
    
    // Friend for stream output (left operand is ostream)
    friend std::ostream& operator<<(std::ostream& os, const Complex& c);
    
    // Friend for stream input
    friend std::istream& operator>>(std::istream& is, Complex& c);
    
    // Friend for scalar multiplication (scalar on left)
    friend Complex operator*(double scalar, const Complex& c);
    
    // Friend for equality comparison
    friend bool operator==(const Complex& a, const Complex& b);
};

std::ostream& operator<<(std::ostream& os, const Complex& c) {
    os << c.real;
    if (c.imag >= 0) os << "+";
    os << c.imag << "i";
    return os;
}

std::istream& operator>>(std::istream& is, Complex& c) {
    is >> c.real >> c.imag;
    return is;
}

Complex operator*(double scalar, const Complex& c) {
    return Complex(scalar * c.real, scalar * c.imag);
}

bool operator==(const Complex& a, const Complex& b) {
    return a.real == b.real && a.imag == b.imag;
}

void demonstrateOperatorOverloading() {
    cout << "\n=== FRIEND FUNCTION FOR OPERATORS ===" << endl;
    
    Complex a(3, 4);
    Complex b(1, -2);
    
    cout << "\nComplex numbers:" << endl;
    cout << "  a = " << a << endl;
    cout << "  b = " << b << endl;
    
    cout << "\nOperations:" << endl;
    cout << "  a + b = " << (a + b) << endl;
    cout << "  2.5 * a = " << (2.5 * a) << endl;  // Friend function (scalar on left)
    cout << "  a == b: " << (a == b ? "true" : "false") << endl;
    
    Complex c(3, 4);
    cout << "  a == c: " << (a == c ? "true" : "false") << endl;
}

// ============================================================================
// SECTION 3: Friend Class
// ============================================================================

// Forward declaration
class BankAccount;

class BankAuditor {
public:
    void audit(const BankAccount& account);
    void setBalance(BankAccount& account, double newBalance);
};

class BankAccount {
private:
    string owner;
    double balance;
    string accountNumber;
    std::vector<string> transactions;
    
public:
    BankAccount(string name, string accNum, double initial)
        : owner(name), accountNumber(accNum), balance(initial) {
        transactions.push_back("Initial deposit: $" + std::to_string(initial));
    }
    
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            transactions.push_back("Deposit: $" + std::to_string(amount));
        }
    }
    
    bool withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            transactions.push_back("Withdrawal: $" + std::to_string(amount));
            return true;
        }
        return false;
    }
    
    double getBalance() const { return balance; }
    
    // BankAuditor can access ALL private members
    friend class BankAuditor;
};

void BankAuditor::audit(const BankAccount& account) {
    cout << "\n  === AUDIT REPORT ===" << endl;
    cout << "  Account: " << account.accountNumber << endl;
    cout << "  Owner: " << account.owner << endl;
    cout << "  Balance: $" << std::fixed << std::setprecision(2) << account.balance << endl;
    cout << "  Transaction History:" << endl;
    for (const auto& t : account.transactions) {
        cout << "    - " << t << endl;
    }
}

void BankAuditor::setBalance(BankAccount& account, double newBalance) {
    // Auditor can modify private data - use responsibly!
    account.balance = newBalance;
    account.transactions.push_back("Audit adjustment to: $" + std::to_string(newBalance));
}

void demonstrateFriendClass() {
    cout << "\n=== FRIEND CLASS ===" << endl;
    
    BankAccount account("John Doe", "ACC-12345", 1000.0);
    account.deposit(500);
    account.withdraw(200);
    
    BankAuditor auditor;
    auditor.audit(account);
    
    cout << "\n  Auditor making correction..." << endl;
    auditor.setBalance(account, 1350.0);
    
    auditor.audit(account);
}

// ============================================================================
// SECTION 4: Friendship Properties Demo
// ============================================================================

class A;  // Forward declaration

class B {
public:
    void accessA(A& a);
};

class A {
private:
    int secretA = 42;
public:
    friend class B;  // B can access A's private
    // A does NOT have access to B's private!
};

class C : public B {
    // C is NOT automatically a friend of A!
    // Friendship is NOT inherited
};

void B::accessA(A& a) {
    cout << "  B accessing A's secret: " << a.secretA << endl;
}

void demonstrateFriendshipProperties() {
    cout << "\n=== FRIENDSHIP PROPERTIES ===" << endl;
    
    A objA;
    B objB;
    
    cout << "\n1. Friendship grants access:" << endl;
    objB.accessA(objA);
    
    cout << "\n2. Friendship is NOT symmetric:" << endl;
    cout << "   B can access A's private members" << endl;
    cout << "   But A cannot access B's private members" << endl;
    
    cout << "\n3. Friendship is NOT inherited:" << endl;
    cout << "   C inherits from B, but C is NOT a friend of A" << endl;
    
    cout << "\n4. Friendship is NOT transitive:" << endl;
    cout << "   If A friends B, and B friends C, C is NOT a friend of A" << endl;
}

// ============================================================================
// SECTION 5: Basic Functor
// ============================================================================

class Adder {
    int value;
public:
    Adder(int v) : value(v) {
        cout << "  [Adder] Created with value " << value << endl;
    }
    
    // This makes it callable like a function
    int operator()(int x) const {
        return x + value;
    }
};

class Multiplier {
    int factor;
public:
    Multiplier(int f) : factor(f) {}
    
    int operator()(int x) const {
        return x * factor;
    }
};

void demonstrateBasicFunctor() {
    cout << "\n=== BASIC FUNCTOR ===" << endl;
    
    cout << "\nCreating functors:" << endl;
    Adder add5(5);
    Adder add10(10);
    Multiplier times3(3);
    
    cout << "\nUsing functors like functions:" << endl;
    cout << "  add5(20) = " << add5(20) << endl;
    cout << "  add10(20) = " << add10(20) << endl;
    cout << "  times3(20) = " << times3(20) << endl;
    
    cout << "\nFunctors with different state produce different results!" << endl;
}

// ============================================================================
// SECTION 6: Functor with State
// ============================================================================

class Counter {
    int count = 0;
public:
    void operator()() {
        ++count;
    }
    
    int getCount() const { return count; }
};

class RunningStats {
    double sum = 0;
    double sumSquares = 0;
    int count = 0;
    
public:
    void operator()(double value) {
        sum += value;
        sumSquares += value * value;
        ++count;
    }
    
    double mean() const { 
        return count > 0 ? sum / count : 0; 
    }
    
    double variance() const {
        if (count < 2) return 0;
        double m = mean();
        return (sumSquares / count) - (m * m);
    }
    
    int getCount() const { return count; }
};

void demonstrateStatefulFunctor() {
    cout << "\n=== STATEFUL FUNCTOR ===" << endl;
    
    cout << "\n1. Counter functor:" << endl;
    Counter counter;
    counter();
    counter();
    counter();
    cout << "  Called 3 times, count = " << counter.getCount() << endl;
    
    cout << "\n2. Running statistics functor:" << endl;
    RunningStats stats;
    std::vector<double> values = {10, 20, 30, 40, 50};
    
    for (double v : values) {
        stats(v);  // Call functor
    }
    
    cout << "  Values: 10, 20, 30, 40, 50" << endl;
    cout << "  Count: " << stats.getCount() << endl;
    cout << "  Mean: " << stats.mean() << endl;
    cout << "  Variance: " << stats.variance() << endl;
}

// ============================================================================
// SECTION 7: Functors with STL Algorithms
// ============================================================================

struct IsEven {
    bool operator()(int n) const {
        return n % 2 == 0;
    }
};

struct IsGreaterThan {
    int threshold;
    IsGreaterThan(int t) : threshold(t) {}
    
    bool operator()(int n) const {
        return n > threshold;
    }
};

struct PrintElement {
    void operator()(int n) const {
        cout << n << " ";
    }
};

struct Square {
    int operator()(int n) const {
        return n * n;
    }
};

void demonstrateFunctorsWithSTL() {
    cout << "\n=== FUNCTORS WITH STL ALGORITHMS ===" << endl;
    
    std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    cout << "\nOriginal: ";
    std::for_each(nums.begin(), nums.end(), PrintElement());
    cout << endl;
    
    // Count even numbers
    int evenCount = std::count_if(nums.begin(), nums.end(), IsEven());
    cout << "Even numbers: " << evenCount << endl;
    
    // Count numbers > 5
    int gt5Count = std::count_if(nums.begin(), nums.end(), IsGreaterThan(5));
    cout << "Numbers > 5: " << gt5Count << endl;
    
    // Transform to squares
    std::vector<int> squares(nums.size());
    std::transform(nums.begin(), nums.end(), squares.begin(), Square());
    cout << "Squares: ";
    std::for_each(squares.begin(), squares.end(), PrintElement());
    cout << endl;
    
    // Remove even numbers
    auto newEnd = std::remove_if(nums.begin(), nums.end(), IsEven());
    nums.erase(newEnd, nums.end());
    cout << "After removing evens: ";
    std::for_each(nums.begin(), nums.end(), PrintElement());
    cout << endl;
}

// ============================================================================
// SECTION 8: STL Predefined Functors
// ============================================================================

void demonstrateSTLFunctors() {
    cout << "\n=== STL PREDEFINED FUNCTORS ===" << endl;
    
    std::vector<int> nums = {5, 2, 8, 1, 9, 3, 7, 4, 6};
    
    cout << "\nOriginal: ";
    for (int n : nums) cout << n << " ";
    cout << endl;
    
    // Sort ascending (default)
    std::sort(nums.begin(), nums.end());
    cout << "Ascending: ";
    for (int n : nums) cout << n << " ";
    cout << endl;
    
    // Sort descending using std::greater
    std::sort(nums.begin(), nums.end(), std::greater<int>());
    cout << "Descending: ";
    for (int n : nums) cout << n << " ";
    cout << endl;
    
    // Accumulate with multiplication
    int product = std::accumulate(nums.begin(), nums.end(), 1, std::multiplies<int>());
    cout << "Product of all: " << product << endl;
    
    // Find using std::equal_to
    nums = {1, 2, 3, 4, 5};
    auto it = std::find_if(nums.begin(), nums.end(), 
                           std::bind(std::equal_to<int>(), std::placeholders::_1, 3));
    if (it != nums.end()) {
        cout << "Found 3 at position " << (it - nums.begin()) << endl;
    }
    
    // Negate all elements
    std::vector<int> negated(nums.size());
    std::transform(nums.begin(), nums.end(), negated.begin(), std::negate<int>());
    cout << "Negated: ";
    for (int n : negated) cout << n << " ";
    cout << endl;
}

// ============================================================================
// SECTION 9: Functors vs Lambdas
// ============================================================================

void demonstrateFunctorsVsLambdas() {
    cout << "\n=== FUNCTORS vs LAMBDAS ===" << endl;
    
    std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // Functor approach
    cout << "\n1. Using functor (IsEven):" << endl;
    int evenFunctor = std::count_if(nums.begin(), nums.end(), IsEven());
    cout << "  Even count: " << evenFunctor << endl;
    
    // Lambda approach (equivalent)
    cout << "\n2. Using lambda:" << endl;
    int evenLambda = std::count_if(nums.begin(), nums.end(), 
                                   [](int n) { return n % 2 == 0; });
    cout << "  Even count: " << evenLambda << endl;
    
    // Functor with state
    cout << "\n3. Functor with state (threshold):" << endl;
    int threshold = 5;
    int gtFunctor = std::count_if(nums.begin(), nums.end(), IsGreaterThan(threshold));
    cout << "  Count > " << threshold << ": " << gtFunctor << endl;
    
    // Lambda capturing state
    cout << "\n4. Lambda capturing state:" << endl;
    int gtLambda = std::count_if(nums.begin(), nums.end(), 
                                  [threshold](int n) { return n > threshold; });
    cout << "  Count > " << threshold << ": " << gtLambda << endl;
    
    cout << "\nNote: Lambdas are actually anonymous functor classes!" << endl;
}

// ============================================================================
// SECTION 10: std::function and Functors
// ============================================================================

class Calculator {
    std::map<string, std::function<double(double, double)>> operations;
    
public:
    Calculator() {
        // Use functors
        operations["add"] = std::plus<double>();
        operations["sub"] = std::minus<double>();
        operations["mul"] = std::multiplies<double>();
        operations["div"] = std::divides<double>();
        
        // Custom lambda
        operations["pow"] = [](double a, double b) { 
            double result = 1;
            for (int i = 0; i < static_cast<int>(b); ++i) {
                result *= a;
            }
            return result;
        };
    }
    
    double calculate(const string& op, double a, double b) {
        if (operations.find(op) != operations.end()) {
            return operations[op](a, b);
        }
        throw std::runtime_error("Unknown operation: " + op);
    }
};

void demonstrateFunctionWrapper() {
    cout << "\n=== std::function WITH FUNCTORS ===" << endl;
    
    Calculator calc;
    
    cout << "\n1. Using stored operations:" << endl;
    cout << "  5 + 3 = " << calc.calculate("add", 5, 3) << endl;
    cout << "  5 - 3 = " << calc.calculate("sub", 5, 3) << endl;
    cout << "  5 * 3 = " << calc.calculate("mul", 5, 3) << endl;
    cout << "  5 / 3 = " << calc.calculate("div", 5, 3) << endl;
    cout << "  2 ^ 8 = " << calc.calculate("pow", 2, 8) << endl;
    
    cout << "\n2. std::function can hold:" << endl;
    cout << "   - Regular functions" << endl;
    cout << "   - Functors" << endl;
    cout << "   - Lambda expressions" << endl;
    cout << "   - std::bind expressions" << endl;
}

// ============================================================================
// SECTION 11: Practical Example - Filter Pipeline
// ============================================================================

template<typename Container, typename... Predicates>
Container filterAll(const Container& input, Predicates... predicates) {
    Container result;
    for (const auto& item : input) {
        bool passesAll = (predicates(item) && ...);  // C++17 fold expression
        if (passesAll) {
            result.push_back(item);
        }
    }
    return result;
}

struct IsPositive {
    bool operator()(int n) const { return n > 0; }
};

struct IsLessThan {
    int limit;
    IsLessThan(int l) : limit(l) {}
    bool operator()(int n) const { return n < limit; }
};

void demonstratePracticalExample() {
    cout << "\n=== PRACTICAL EXAMPLE: FILTER PIPELINE ===" << endl;
    
    std::vector<int> nums = {-5, 10, 3, -2, 8, 15, 7, -1, 20, 4};
    
    cout << "\nOriginal: ";
    for (int n : nums) cout << n << " ";
    cout << endl;
    
    // Filter: positive AND less than 10
    auto filtered = filterAll(nums, IsPositive(), IsLessThan(10));
    
    cout << "Positive AND < 10: ";
    for (int n : filtered) cout << n << " ";
    cout << endl;
    
    // Using with lambdas
    auto filtered2 = filterAll(nums, 
                               [](int n) { return n > 0; },
                               [](int n) { return n % 2 == 0; });
    
    cout << "Positive AND even: ";
    for (int n : filtered2) cout << n << " ";
    cout << endl;
}

// ============================================================================
// SECTION 12: Friend Functions in Templates
// ============================================================================

template<typename T>
class Container {
    T value;
    
public:
    Container(T v) : value(v) {}
    
    // Friend function template
    template<typename U>
    friend std::ostream& operator<<(std::ostream& os, const Container<U>& c);
    
    // Friend function for same type
    friend bool operator==(const Container& a, const Container& b) {
        return a.value == b.value;
    }
};

template<typename U>
std::ostream& operator<<(std::ostream& os, const Container<U>& c) {
    return os << "Container(" << c.value << ")";
}

void demonstrateTemplateFriends() {
    cout << "\n=== FRIEND FUNCTIONS IN TEMPLATES ===" << endl;
    
    Container<int> c1(42);
    Container<int> c2(42);
    Container<string> c3("Hello");
    
    cout << "\n1. Stream output friend:" << endl;
    cout << "  " << c1 << endl;
    cout << "  " << c3 << endl;
    
    cout << "\n2. Equality friend:" << endl;
    cout << "  c1 == c2: " << (c1 == c2 ? "true" : "false") << endl;
}

// ============================================================================
// MAIN
// ============================================================================

int main() {
    cout << "╔══════════════════════════════════════════════════════════════╗" << endl;
    cout << "║     FRIEND FUNCTIONS, FRIEND CLASSES & FUNCTORS EXAMPLES     ║" << endl;
    cout << "╚══════════════════════════════════════════════════════════════╝" << endl;
    
    demonstrateFriendFunctions();
    demonstrateOperatorOverloading();
    demonstrateFriendClass();
    demonstrateFriendshipProperties();
    demonstrateBasicFunctor();
    demonstrateStatefulFunctor();
    demonstrateFunctorsWithSTL();
    demonstrateSTLFunctors();
    demonstrateFunctorsVsLambdas();
    demonstrateFunctionWrapper();
    demonstratePracticalExample();
    demonstrateTemplateFriends();
    
    cout << "\n═══════════════════════════════════════════════════════════════" << endl;
    cout << "All examples completed!" << endl;
    
    return 0;
}
Examples - C++ Tutorial | DeepML