Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Friend Functions and Friend Classes in C++

Table of Contents

  1. What is a Friend Function
  2. What is a Friend Class
  3. Friend Functions and Encapsulation
  4. Why Friend Functions Cannot Be Const
  5. Friend Functions and Inheritance
  6. Accessing Static Private Members
  7. Scope of Friend Functions
  8. Useful Cases for Friend Functions

What is a Friend Function

A friend function is a function that is granted access to the private and protected members of a class, even though it is not a member of that class. It is declared inside the class using the friend keyword but defined outside the class scope.

Global Function as Friend

A global function can be declared as a friend to access private members of a class.

#include <iostream>
using namespace std;

class Box {
private:
    int width;
    
public:
    Box(int w) : width(w) {}
    
    // Declare global function as friend
    friend void displayWidth(Box b);
};

// Define the friend function
void displayWidth(Box b) {
    // Can access private member 'width'
    cout << "Width of box: " << b.width << endl;
}

int main() {
    Box box(10);
    displayWidth(box);  // Output: Width of box: 10
    return 0;
}

Friend Function Inside Class

You can define a friend function directly inside the class body. The function is still not a member function, but it’s defined inline within the class. It can be called from outside using argument-dependent lookup (ADL).

#include <iostream>
using namespace std;

class Box {
private:
    int width;
    int height;
    
public:
    Box(int w, int h) : width(w), height(h) {}
    
    // Friend function defined inside the class
    friend void displayBox(Box b) {
        // Can access private members
        cout << "Box dimensions: " << b.width << " x " << b.height << endl;
    }
    
    // Another friend function defined inside
    friend void compareBoxes(Box b1, Box b2) {
        cout << "Box1 area: " << (b1.width * b1.height) << endl;
        cout << "Box2 area: " << (b2.width * b2.height) << endl;
        
        if (b1.width * b1.height > b2.width * b2.height)
            cout << "Box1 is larger" << endl;
        else
            cout << "Box2 is larger" << endl;
    }
};

int main() {
    Box box1(10, 20);
    Box box2(15, 15);
    
    // Call friend functions - they are NOT member functions
    // so we don't use box1.displayBox()
    displayBox(box1);              // Output: Box dimensions: 10 x 20
    compareBoxes(box1, box2);      // Compares both boxes
    
    return 0;
}

Important Notes:

  • Even though defined inside the class, these are not member functions
  • They are called directly by name, not through an object (e.g., displayBox(box1) not box1.displayBox())
  • They don’t have a this pointer
  • They are found via argument-dependent lookup (ADL) when called

↑ Back to Table of Contents


What is a Friend Class

A friend class is a class whose all member functions have access to the private and protected members of another class. It is declared using the friend keyword.

Entire Class as Friend

When an entire class is declared as a friend, all its member functions can access private members.

Important: Friendship is not mutual. If class A is a friend of class B, it doesn’t mean B is automatically a friend of A.

#include <iostream>
using namespace std;

class Box {
private:
    int width;
    int height;
    
public:
    Box(int w, int h) : width(w), height(h) {}
    
    // Declare entire class as friend
    friend class BoxPrinter;
};

class BoxPrinter {
private:
    string printerName;
    
public:
    BoxPrinter(string name) : printerName(name) {}
    
    void printDimensions(Box b) {
        // BoxPrinter can access Box's private members
        cout << "Width: " << b.width << ", Height: " << b.height << endl;
    }
    
    void printArea(Box b) {
        cout << "Area: " << (b.width * b.height) << endl;
    }
};

// Box CANNOT access BoxPrinter's private members
void testBox(Box b, BoxPrinter printer) {
    // cout << printer.printerName << endl;  // Error: cannot access private member
}

int main() {
    Box box(10, 20);
    BoxPrinter printer("HP-Printer");
    printer.printDimensions(box);  // Output: Width: 10, Height: 20
    printer.printArea(box);        // Output: Area: 200
    return 0;
}

Key Points about Friendship:

  • Friendship is one-way: BoxPrinter can access Box’s private members, but Box cannot access BoxPrinter’s private members
  • Friendship must be explicitly granted: If you want mutual access, both classes must declare each other as friends
  • Friendship is not transitive: If A is a friend of B, and B is a friend of C, it doesn’t mean A is a friend of C

Only One Member Function as Friend

You can declare only specific member functions of a class as friends, rather than the entire class.

#include <iostream>
using namespace std;

class Box;  // Forward declaration

class Analyzer {
public:
    void analyzeBox(Box b);      // Will be friend
    void processBox(Box b);      // Will NOT be friend
};

class Box {
private:
    int width;
    
public:
    Box(int w) : width(w) {}
    
    // Only analyzeBox is friend, not processBox
    friend void Analyzer::analyzeBox(Box b);
};

void Analyzer::analyzeBox(Box b) {
    cout << "Analyzing box with width: " << b.width << endl;  // Works
}

void Analyzer::processBox(Box b) {
    // cout << b.width << endl;  // Error: cannot access private member
    cout << "Processing box..." << endl;
}

int main() {
    Box box(15);
    Analyzer analyzer;
    analyzer.analyzeBox(box);   // Output: Analyzing box with width: 15
    analyzer.processBox(box);   // Output: Processing box...
    return 0;
}

↑ Back to Table of Contents


Friend Functions and Encapsulation

While friend functions allow access to private and protected data members, which technically breaks encapsulation, they are still useful in certain scenarios:

  1. Operator Overloading: When overloading binary operators (like +, -, <<, >>) where the left operand is not a class object.

  2. Bridge Between Two Classes: When two tightly coupled classes need to share data efficiently without exposing it publicly.

  3. Testing and Debugging: Unit tests may need access to internal state without making everything public.

  4. Performance Optimization: Avoiding getter/setter overhead when frequent access is needed between closely related classes.

  5. Legacy Code Integration: When integrating with existing code that requires direct access to internal structures.

  6. Implementation of Certain Design Patterns: Patterns like Iterator or Visitor may benefit from friend access.

  7. Symmetric Operations: When operations need to treat multiple objects equally (like comparing two objects).

↑ Back to Table of Contents


Why Friend Functions Cannot Be Const

A friend function cannot be declared as const because:

  1. Not a Member Function: The const keyword in a member function indicates that the function doesn’t modify the object it’s called on (the implicit this pointer points to a const object).

  2. No Implicit this Pointer: Friend functions are not member functions, so they don’t have an implicit this pointer to qualify as const or non-const.

  3. Parameter-Based Qualification: If a friend function shouldn’t modify an object, you pass that object as a const reference or pointer parameter.

class Box {
private:
    int width;
    
public:
    Box(int w) : width(w) {}
    
    // Correct: Pass const reference if function shouldn't modify
    friend void display(const Box& b);
    
    // Incorrect syntax: friend functions can't be const
    // friend void display(Box b) const;  // Error!
};

void display(const Box& b) {
    cout << b.width << endl;
    // b.width = 10;  // Error: cannot modify const object
}

↑ Back to Table of Contents


Friend Functions and Inheritance

Friend functions are not inherited by derived classes. Friendship must be explicitly granted by each class.

#include <iostream>
using namespace std;

class Base {
private:
    int baseData;
    
public:
    Base(int d) : baseData(d) {}
    friend void showBase(Base b);
};

class Derived : public Base {
private:
    int derivedData;
    
public:
    Derived(int b, int d) : Base(b), derivedData(d) {}
    // showBase is NOT automatically a friend of Derived
};

void showBase(Base b) {
    cout << "Base data: " << b.baseData << endl;  // Works
}

void showDerived(Derived d) {
    // cout << d.baseData << endl;     // Error: cannot access
    // cout << d.derivedData << endl;  // Error: cannot access
}

int main() {
    Base b(10);
    Derived d(20, 30);
    
    showBase(b);   // Works
    showBase(d);   // Works (object slicing to Base)
    return 0;
}

Key Point: If you want a friend function to access derived class members, you must explicitly declare it as a friend in the derived class as well.

↑ Back to Table of Contents


Accessing Static Private Members

Friend functions can access static private data members just like instance members.

#include <iostream>
using namespace std;

class Counter {
private:
    static int count;
    int instanceId;
    
public:
    Counter() {
        instanceId = ++count;
    }
    
    friend void displayStatistics();
    friend void displayInstance(Counter c);
};

// Initialize static member
int Counter::count = 0;

void displayStatistics() {
    // Access static private member
    cout << "Total objects created: " << Counter::count << endl;
}

void displayInstance(Counter c) {
    // Access both static and instance private members
    cout << "Instance ID: " << c.instanceId << endl;
    cout << "Total count: " << Counter::count << endl;
}

int main() {
    Counter c1, c2, c3;
    
    displayStatistics();      // Output: Total objects created: 3
    displayInstance(c2);      // Output: Instance ID: 2, Total count: 3
    
    return 0;
}

↑ Back to Table of Contents


Scope of Friend Functions

The scope of a friend function depends on where it is defined:

  1. Global Scope: If defined outside any class, it has global scope.

  2. Namespace Scope: If defined within a namespace, it belongs to that namespace.

  3. Not in Class Scope: Even though declared inside a class, friend functions are not members of that class and don’t belong to the class scope.

  4. Access Rules: Friend functions can be called like any other function based on their actual scope, not through the class.

#include <iostream>
using namespace std;

namespace MyNamespace {
    class Box {
    private:
        int width;
        
    public:
        Box(int w) : width(w) {}
        friend void display(Box b);  // Declared in class
    };
    
    // Defined in namespace scope
    void display(Box b) {
        cout << "Width: " << b.width << endl;
    }
}

int main() {
    MyNamespace::Box box(50);
    
    // Call using namespace scope, not class scope
    MyNamespace::display(box);  // Correct
    // box.display();           // Error: not a member function
    
    return 0;
}

Important: Friend functions are called directly by their name (with appropriate namespace qualification if needed), not as member functions through an object.

↑ Back to Table of Contents


Useful Cases for Friend Functions

Friend functions are particularly useful in the following scenarios:

1. Operator Overloading

Friend functions are commonly used for operator overloading, especially for binary operators and I/O stream operators where the left operand is not your class object.

Note: Operator overloading will be covered in detail in a separate chapter.

2. Implementing Bridge Between Tightly Coupled Classes

When two classes need to work together closely and share internal state.

class Engine;

class Car {
private:
    string model;
    
public:
    Car(string m) : model(m) {}
    friend class Engine;  // Engine can access Car's internals
};

class Engine {
private:
    int horsepower;
    
public:
    Engine(int hp) : horsepower(hp) {}
    
    void diagnose(Car& car) {
        cout << "Diagnosing " << car.model << " with " 
             << horsepower << "hp engine" << endl;
    }
};

3. Factory Functions

Friend functions can act as factory methods that construct objects with access to private constructors.

Note: Factory patterns will be covered in detail in a separate chapter.

4. Unit Testing

Friend functions and classes allow test code to access private members without making them public, enabling thorough testing while maintaining encapsulation in production code.

#include <iostream>
using namespace std;

class BankAccount {
private:
    double balance;
    string accountNumber;
    
public:
    BankAccount(string accNum, double b) : accountNumber(accNum), balance(b) {}
    
    void deposit(double amount) {
        if (amount > 0)
            balance += amount;
    }
    
    bool withdraw(double amount) {
        if (amount > 0 && balance >= amount) {
            balance -= amount;
            return true;
        }
        return false;
    }
    
    #ifdef UNIT_TEST
    friend class AccountTester;
    #endif
};

#ifdef UNIT_TEST
class AccountTester {
public:
    static void testBalance() {
        BankAccount acc("ACC123", 1000.0);
        
        // Direct access to private members for testing
        cout << "Initial balance: " << acc.balance << endl;
        
        acc.deposit(500);
        cout << "After deposit: " << acc.balance << endl;
        
        acc.withdraw(200);
        cout << "After withdrawal: " << acc.balance << endl;
        
        // Verify internal state
        if (acc.balance == 1300.0)
            cout << "Test PASSED!" << endl;
        else
            cout << "Test FAILED!" << endl;
    }
};
#endif

int main() {
    #ifdef UNIT_TEST
    AccountTester::testBalance();
    #else
    BankAccount acc("ACC456", 2000.0);
    acc.deposit(500);
    acc.withdraw(300);
    cout << "Production mode - private members protected" << endl;
    #endif
    
    return 0;
}

Benefits:

  • Test code can verify internal state without exposing it publicly
  • Conditional compilation keeps test access separate from production code
  • Maintains encapsulation while enabling comprehensive testing

↑ Back to Table of Contents


Summary

Friend functions and friend classes provide controlled access to private members when:

  • Encapsulation needs can be met through careful design
  • The relationship between classes is well-defined and stable
  • Performance or design patterns require direct access
  • Operator overloading or symmetric operations are needed

Use them judiciously to maintain good object-oriented design principles while solving practical problems that arise in real-world programming.