Hubbry Logo
Friend classFriend classMain
Open search
Friend class
Community hub
Friend class
logo
7 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
Friend class
Friend class
from Wikipedia

A friend class in C++ can access the private and protected members of the class in which it is declared as a friend.[1] A significant use of a friend class is for a part of a data structure, represented by a class, to provide access to the main class representing that data structure. The friend class mechanism allows to extend the storage and access to the parts, while retaining proper encapsulation as seen by the users of the data structure.

Similar to a friend class, a friend function is a function that is given access to the private and protected members of the class in which it is declared as a friend.

Since C++26, C++ supports "variadic friends" (i.e. friend classes that come from variadic templates).[2]

Example

[edit]

The following example demonstrates the use of a friend-class for a graph data structure, where the graph is represented by the main class Graph, and the graph's vertices are represented by the class Vertex.

import std;

template <typename T>
using SharedPtr = std::shared_ptr<T>;
using String = std::string;
template <typename T>
using UniquePtr = std::unique_ptr<T>;
template <typename T>
using HashSet = std::unordered_set<T>; // default-specialize the remaining template-parameters

class Vertex {
private:
    // Vertex gives access-rights to Graph.
    friend class Graph;

    HashSet<SharedPtr<Vertex>> edges;
    String name;
    using EdgesIterator = decltype(edges.cbegin());
public:
    explicit Vertex(const String& name): 
        name{std::move(name)} {}

    EdgesIterator begin() const { 
        return edges.cbegin(); 
    }

    EdgesIterator end() const { 
        return edges.cend(); 
    }

    String getName() const { 
        return name; 
    }
};

class Graph {
private:
    HashSet<SharedPtr<Vertex>> vertices;
    using VerticesIterator = decltype(vertices.cbegin());
public:
    ~Graph() {
        while (!vertices.empty()) {
           VerticesIterator vertex = vertices.begin();
           removeVertex(*vertex);
        }
    }

    void addVertex(const String& name) {
        UniquePtr<Vertex> vertex = std::make_unique<Vertex>(name);
        VerticesIterator iter = vertices.insert(vertex.get());
    }

    void removeVertex(SharedPtr<Vertex> vertex) {
        vertices.erase(vertex);
    }

    void addEdge(SharedPtr<Vertex> from, SharedPtr<Vertex> to) {
        // Graph can access Vertex's private fields because Vertex declared Graph as
        // a friend.
        from->edges.insert(to);
    }

    VerticesIterator begin() const { 
        return vertices.cbegin(); 
    }

    VerticesIterator end() const { 
        return vertices.cend(); 
    }
};

Using variadic friends:

import std;

template <typename... Friends>
class ClassWithFriends {
private:
    int secretValue;

    friend Friends...;
public:
    explicit ClassWithFriends(int secret):
        secretValue{secret} {}
};

class A {
public:
    void readSecret(const class ClassWithFriends<A, B>& instance) const {
        std::println("Inspecting secret value from A: {}", instance.secretValue);
    }
};

class B {
public:
    void readSecret(const class ClassWithFriends<A, B>& instance) const {
        std::println("Inspecting secret value from B: {}", instance.secretValue);
    }
};

int main(int argc, char* argv[]) {
    ClassWithFriends<A, B> secretHolder(135);
    A a;
    B b;
    a.readSecret(secretHolder);
    b.readSecret(secretHolder);
}

Encapsulation

[edit]

A proper use of friend classes increases encapsulation, because it allows to extend the private access of a data-structure to its parts --- which the data-structure owns --- without allowing private access to any other external class. This way the data-structure stays protected against accidental attempts at breaking the invariants of the data-structure from outside.

It is important to notice that a class cannot give itself access to another class's private part; that would break encapsulation. Rather, a class gives access to its own private parts to another class --- by declaring that class as a friend. In the graph example, Graph cannot declare itself a friend of Vertex. Rather, Vertex declares Graph a friend, and so provides Graph an access to its private fields.

The fact that a class chooses its own friends means that friendship is not symmetric in general. In the graph example, Vertex cannot access private fields of Graph, although Graph can access private fields of Vertex.

Alternatives

[edit]

A similar, but not equivalent, language feature is given by C#'s internal access modifier keyword, which allows classes inside the same assembly to access the private parts of other classes. This corresponds to marking each class a friend of another in the same assembly; friend classes are more fine-grained.

Programming languages which lack support for friend classes, or a similar language feature, will have to implement workarounds to achieve a safe part-based interface to a data-structure. Examples of such workarounds are:

  • Make the parts' fields public. This solution decreases encapsulation by making it possible to violate invariants of the data-structure from outside.
  • Move all mutable structural data away from the part to the data-structure, and introduce indirection back from each part to its data-structure. This solution changes the organization of the data structure, and increases memory consumption in cases where there would otherwise be no need for this information.

Properties

[edit]
  • Friendships are not symmetric – if class A is a friend of class B, class B is not automatically a friend of class A.
  • Friendships are not transitive – if class A is a friend of class B, and class B is a friend of class C, class A is not automatically a friend of class C.
  • Friendships are not inherited – if class Base is a friend of class X, subclass Derived is not automatically a friend of class X; and if class X is a friend of class Base, class X is not automatically a friend of subclass Derived. However, if class Y is a friend of subclass Derived, class Y will also have access to protected portions of class Base, just as subclass Derived does.

References

[edit]
[edit]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
In C++, a friend class is a class that is granted special access privileges to the private and protected members of another class, allowing it to interact closely without violating encapsulation principles. This feature, introduced in the original C++ standard, enables selective sharing of internal data and methods between related classes, such as in or tightly coupled designs. The declaration of a friend class occurs within the body of the granting class using the friend keyword followed by the class name, as in friend class OtherClass;. This syntax does not require the friend class to be fully defined at the point of declaration, though it must be known by the time its members access the granted class's internals. Once declared, all member functions of the friend class—including both instance and static methods—gain unrestricted access to the private and protected elements of the host class, treating them as if they were public. Friendship in C++ is a deliberate design choice to balance with necessary interoperability, but it comes with strict rules to prevent unintended exposure. Notably, friendship is neither inherited nor transitive: if Class A is a friend of Class B, then derived classes from A do not automatically become friends of B, and friends of A are not friends of B. It is also not symmetric, meaning that declaring A as a friend of B does not make B a friend of A unless explicitly stated. These properties ensure that access remains controlled and explicit, promoting maintainable code while avoiding the pitfalls of overly permissive interfaces. In practice, friend classes are used sparingly to maintain strong encapsulation, often in scenarios like implementing binary operators (e.g., operator<< for streams) or managing complex data structures where one class needs deep integration with another. Overuse can lead to fragile dependencies, so modern C++ guidelines recommend preferring public interfaces, getters/setters, or templates where possible to minimize reliance on friends. Since C++11, enhancements to friend declarations allow more flexible type specifiers, such as using typename for dependent types in templates, further refining its utility in generic programming.

Definition and Purpose

Definition

In C++, a friend class is a mechanism that permits a designated class to access the private and protected members of another class, thereby overriding the standard access restrictions imposed by the object's encapsulation model. This access grant is explicit and one-way, meaning the befriended class can interact with the internal details of the host class as if they were its own members, without requiring public exposure of those elements. A key distinction exists between friend classes and friend functions: while a friend function provides targeted access to private and protected members for a specific non-member function, a friend class extends this privilege to all member functions of the friend class, enabling comprehensive interaction with the host class's internals. This broader scope makes friend classes suitable for scenarios where an entire collaborating class needs unfettered access, rather than isolated operations. The concept of friend classes was introduced by Bjarne Stroustrup during the development of "C with Classes" from 1979 to 1983, as part of the initial protection model to enable flexible access control without compromising the overall design of object-oriented features. Initially, only friend classes were supported, granting access to all member functions of the befriended class; this was later expanded to include individual friend functions. The design drew inspiration from operating system access rights, aiming to balance security with practical utility in large-scale programming. Friend classes operate against the backdrop of C++'s fundamental access specifiers—private and protected—which restrict member visibility to within the class or its derived classes, respectively, to enforce data hiding and modularity. By declaring a friend relationship, these baseline controls are selectively bypassed, allowing controlled exceptions to the encapsulation principle without altering the specifiers themselves.

Purpose

The friend class mechanism in C++ primarily enables controlled access to private and protected members of a class by another class, facilitating scenarios where tight integration is necessary without broadly compromising encapsulation. One key purpose is to support operator overloading, particularly for non-member operators like stream insertion (operator<<), which require access to internal data to serialize objects without exposing it publicly. This approach allows classes to integrate seamlessly with standard I/O streams, for example, by declaring a non-member operator<< function as a friend to allow access to private members for output to streams like std::ostream. Another motivation is to permit tightly coupled classes, such as iterators accessing the internals of container classes, ensuring efficient traversal without public interfaces that could invite misuse. In custom container designs, the iterator class is often declared as a friend of the container to manipulate private data structures directly, mirroring practices in the C++ standard library where iterators interact deeply with container implementations. Additionally, friend classes support unit testing by granting test classes access to private members, allowing verification of internal behavior without adding public getters or setters that might alter the class's external contract. These uses maintain encapsulation for unrelated external code by limiting access to designated friends, thereby avoiding the need for excessive public accessor methods that could increase coupling and maintenance overhead. In design patterns like the visitor pattern, friend declarations enable external operations to traverse and modify object hierarchies without relying on inheritance or public exposure. However, this feature is motivated by its selectivity; it is not intended for loose coupling, as friending creates explicit dependencies that can undermine information hiding principles central to object-oriented design.

Syntax and Declaration

Friend Class Declaration

In C++, a friend class declaration specifies that an entire class is granted access to the private and protected members of another class, allowing the friend class to interact closely with the befriended class's internals. The syntax for declaring a class as a friend uses the friend keyword followed by class and the name of the friend class, typically written as friend class ClassName; within the body of the befriended class's definition. This declaration can employ an elaborated-type-specifier (e.g., friend class ClassName;), which is valid until C++26, or a simple-type-specifier (e.g., friend ClassName;) introduced in C++11, though the latter requires the class to be previously declared to avoid errors. The placement of the friend class declaration occurs inside the befriended class's definition, where it can appear in the public, private, or protected sections; access specifiers have no effect on the meaning of friend declarations. If the friend class has not been defined or forward-declared prior to the friend declaration, the elaborated-type-specifier form implicitly provides a forward declaration for the friend class, enabling its use without a separate prior declaration in the same scope. In contrast, the simple-type-specifier form does not introduce a forward declaration and will be ignored by the compiler if the named type is not recognized as a class at that point, potentially leading to compilation failure if access is attempted later. For mutual friendship between two classes, each must explicitly declare the other as a friend using forward declarations to resolve circular dependencies, such as forward-declaring both classes before their definitions and including reciprocal friend class statements in each. The scope of a friend class declaration encompasses the entire friend class, granting it unrestricted access to all private and protected non-static and static members of the befriended class, including data members, member functions, and nested types, without needing inheritance or other mechanisms. This access applies to instances of the friend class operating on instances of the befriended class, and it is not inherited by derived classes of the friend nor transitive to other friends. Friend class declarations are resolved entirely at compile-time, introducing no runtime overhead as the access permissions are enforced statically by the compiler during type checking and code generation. If the friend class name is ambiguous—such as due to namespace conflicts, multiple declarations, or injected class names—the compiler will issue an error, preventing compilation until the ambiguity is resolved through qualification or scoping adjustments.

Friend Function Declaration

A friend function declaration in C++ is specified within the body of a class definition using the friend keyword followed by a function declaration, which grants that non-member function access to the private and protected members of the enclosing class as if it were a member function. The basic syntax is friend ReturnType functionName(Parameters);, where the declaration must appear inside the class but the function body is typically defined outside the class unless it is an inline definition. Variations of friend function declarations include support for overloaded functions, where multiple declarations with the same name but different parameters can each be granted friendship independently; member functions of another class, declared as friend OtherClass::MemberFunction(Parameters);; and global functions, which are commonly used for operator overloading to enable non-member access to private data. These declarations allow precise control over which specific functions receive elevated access privileges. Key rules governing friend function declarations stipulate that the function must be defined outside the class unless provided as an inline friend definition, and no storage class specifiers (such as static or extern) are permitted in the declaration. Access is granted exclusively to the exact function signature declared as a friend, meaning changes to parameters or return type would require a separate friendship declaration. When a class is defined within a namespace, a friend function declaration integrates by placing the friend's name in the innermost enclosing namespace scope, though it is not visible outside the class unless explicitly redeclared at the namespace level. This ensures namespace isolation while allowing the friend to operate within the appropriate scope for accessing class members.

Examples

Basic Example

A basic example of a friend class in C++ involves a class that stores private dimensions and declares another class, Printer, as a friend to access those dimensions directly for computation and output. Consider the following code snippet, which demonstrates the declaration and usage of a friend class:

cpp

#include <iostream> class [Box](/page/Box) { private: double width; double height; public: [Box](/page/Box)(double w, double h) : width(w), height(h) {} // Declare [Printer](/page/Declare) as a friend class friend class Printer; }; class Printer { public: void printArea(const [Box](/page/Box)& box) { // Friend class accesses private members directly double area = box.width * box.height; std::cout << "Area: " << area << std::endl; } }; int main() { Box myBox(4.0, 5.0); Printer printer; printer.printArea(myBox); // Outputs: Area: 20 return 0; }

#include <iostream> class [Box](/page/Box) { private: double width; double height; public: [Box](/page/Box)(double w, double h) : width(w), height(h) {} // Declare [Printer](/page/Declare) as a friend class friend class Printer; }; class Printer { public: void printArea(const [Box](/page/Box)& box) { // Friend class accesses private members directly double area = box.width * box.height; std::cout << "Area: " << area << std::endl; } }; int main() { Box myBox(4.0, 5.0); Printer printer; printer.printArea(myBox); // Outputs: Area: 20 return 0; }

This example begins with the Box class declaration, where width and height are private members initialized via a public constructor. The friend declaration friend class Printer; inside Box grants the Printer class unrestricted access to Box's private members, as defined in the C++ language specification. Next, the Printer class is defined with a public member function printArea that takes a Box object by const reference. Within this function, Printer directly accesses box.width and box.height to compute the area, bypassing the need for public getter methods in Box. In the main function, a Box object is instantiated with dimensions 4.0 and 5.0, a Printer object is created, and printArea is invoked on the Box instance. When compiled and executed (e.g., using a C++11 or later compliant compiler like g++ with command g++ -std=c++11 example.cpp -o example && ./example), the program outputs "Area: 20" without compilation errors related to access violations. This confirms the friend relationship enables Printer to treat Box's private members as if they were public, while maintaining encapsulation for non-friend code.

Operator Overloading Example

One common application of friend functions in C++ is overloading binary operators like addition (+) for user-defined types, such as complex numbers, where direct access to private members is required for efficient implementation. Consider a Complex class that encapsulates real and imaginary parts as private double members to maintain encapsulation. The class declares a global operator+ as a friend function, granting it permission to read and manipulate these private fields without public getter methods. Here is a representative implementation:

cpp

#include <iostream> class Complex { private: double real; double imag; public: Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {} // Declare friend function for operator+ friend Complex operator+(const Complex& a, const Complex& b); // Helper function to output the complex number void print() const { std::cout << "(" << real << " + " << imag << "i)" << std::endl; } }; // Definition of the friend operator+ function Complex operator+(const Complex& a, const Complex& b) { return Complex(a.real + b.real, a.imag + b.imag); }

#include <iostream> class Complex { private: double real; double imag; public: Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {} // Declare friend function for operator+ friend Complex operator+(const Complex& a, const Complex& b); // Helper function to output the complex number void print() const { std::cout << "(" << real << " + " << imag << "i)" << std::endl; } }; // Definition of the friend operator+ function Complex operator+(const Complex& a, const Complex& b) { return Complex(a.real + b.real, a.imag + b.imag); }

In this setup, the friend declaration inside the Complex class allows the non-member operator+ to access a.real, a.imag, b.real, and b.imag directly, enabling the addition of corresponding components to produce a new Complex object. The function takes two const references to avoid unnecessary copies, promoting efficiency. To use this overloaded operator, objects can be combined using natural infix notation, such as Complex c3 = c1 + c2;, where c1 and c2 are instances of Complex. For example, if c1 is initialized as Complex(3.0, 4.0) and c2 as Complex(1.0, 2.0), then c3 evaluates to Complex(4.0, 6.0), representing the sum (3 + 4i) + (1 + 2i) = (4 + 6i). Invoking c3.print() would output: (4 + 6i). This computation occurs seamlessly through the friend function's access to private data. The use of a friend function here ensures symmetry in operator overloading: unlike a member function version (e.g., Complex operator+(const Complex& other)), which treats the left operand as this and requires the right operand to be of the same type, the non-member friend allows the left operand to be a built-in type or another class, facilitating expressions like c3 = 2.0 + c2. This design preserves encapsulation for the class while enabling expressive, intuitive syntax for arithmetic operations on complex numbers.

Design Implications

Encapsulation Effects

Encapsulation in object-oriented programming, particularly in C++, refers to the bundling of data and methods within a class while restricting direct access to the internal implementation details through access specifiers such as private and protected. This mechanism enforces abstraction by preventing unauthorized access to an object's volatile parts, allowing external code to interact only with stable interfaces. Friend declarations in C++ introduce a selective breach in this encapsulation barrier by granting specific external functions or classes access to private and protected members of the friending class. This controlled access is intended for trusted entities that require it for legitimate operations, such as operator overloading or closely coupled components in a library, but it inherently increases coupling between the involved classes, as modifications to private members may necessitate changes in friend code. If friend relationships proliferate across multiple classes, they can create implicit dependencies that complicate maintenance and refactoring efforts. On the positive side, friends enable necessary internal access without exposing members publicly or relying on inheritance, thereby supporting modular designs where encapsulation is preserved for untrusted code while allowing efficient interactions in trusted contexts, such as within software frameworks. However, this approach deviates from the strict "black box" ideal of encapsulation, where a class's internals remain completely hidden from all external entities, potentially making the system's dependencies less transparent and more prone to unintended interactions. Overuse of friends can exacerbate these issues by fostering tightly coupled designs that hinder long-term maintainability.

Key Properties

Friend declarations in C++ grant a function or another class the same level of access to the private and protected members of the enclosing class as its own member functions would have. However, since friend functions are non-member functions, they do not receive an implicit this pointer to the object whose members they access, requiring explicit object parameters for such operations. Friendship is not symmetric, meaning that if class A declares class B as a friend, class B does not automatically have access to A's private members unless A is similarly declared a friend within B. Furthermore, friendship is neither transitive nor inherited; a friend of a friend is not automatically a friend, and derived classes of a friend class do not inherit that friendship status. In the context of templates, declaring a class template as a friend of an enclosing class makes every specialization of that template—whether implicitly instantiated, explicitly specialized, or partially specialized—a friend, provided the declaration uses the template name without arguments. Declaring specific instantiations, such as friend class MyTemplate<int>, requires explicit naming, while partial specializations cannot be directly specified in friend declarations and may lead to compilation errors if attempted. For function templates as friends, the declaration grants friendship to all specializations that match the declared signature, necessitating precise specification to avoid unintended access grants. A friend declaration places the name of the friended entity into the innermost enclosing of the class but does not make it visible for name lookup from outside the class body unless the entity is separately declared in that scope. Consequently, friend declarations do not imply the prior existence of the friended function or class and have no impact on external name resolution. The friend feature is a core part of the C++ standard, introduced in C++98 and retained through C++26, ensuring portability across all standards-compliant compilers without variations in core behavior.

Alternatives and Comparisons

Within C++

In C++, several mechanisms provide controlled access to class members without relying on friend declarations, thereby maintaining encapsulation through different structural approaches. These alternatives are particularly useful when the relationship between classes does not necessitate the explicit access privileges granted by friends, allowing for more modular and extensible designs. Getters and setters, implemented as public member functions, offer a common way to achieve read and write access to private data members while preserving encapsulation. A getter function returns the value of a private member, and a modifies it, often with validation logic to enforce invariants. This approach adds some but ensures that internal representation remains hidden from clients, promoting as a core principle of object-oriented design. For instance, in a class representing a point, a getX() method could expose the x-coordinate without revealing how it is stored. Inheritance provides another alternative by allowing derived classes to access protected members of a base class, which are inaccessible to unrelated classes. This is suitable for "is-a" relationships where extension or specialization is intended, such as a DerivedShape class accessing protected logic in a BaseShape. However, it is not applicable for arbitrary collaborations outside hierarchical structures, as it imposes inheritance-specific constraints like the . Protected access thus supports polymorphism without the tight coupling of friends. Composition, particularly through nested classes, enables tighter integration by embedding one class within another. A nested class, declared inside an enclosing class, has the same access rights as any member of the enclosing class, including to its private and protected members. This allows the nested class to manipulate the outer class's internals directly, facilitating implementation details like or helper structures without exposing them publicly. For example, a container class might define a private nested class that accesses its private storage. While this promotes locality, the nested class must still respect the enclosing class's access specifiers for its own members. Making members public offers direct access but undermines encapsulation by exposing implementation details, making it rarely recommended except for simple data structures like POD types. In contrast, friends are preferable for non-hierarchical or symmetric couplings, such as between peer classes in a graph algorithm, where inheritance would be inappropriate and getters might introduce excessive indirection. These alternatives thus trade off flexibility against the risks of unintended access, with friends reserved for cases where no cleaner structural fit exists.

In Other Languages

In , there is no direct equivalent to C++'s friend class mechanism for granting selective access to private members across unrelated classes. Instead, developers achieve similar selective visibility through package-private access, which is the default modifier allowing classes within the same package to access each other's package-private members without explicit declaration. Inner classes provide another approach, as a non-static inner class can access the private members of its enclosing outer class, enabling controlled exposure in nested structures. Python lacks formal access modifiers or a friend-like construct, relying instead on to signal intended privacy levels. Attributes prefixed with a single underscore (e.g., _private) are considered protected by convention, discouraging but not preventing external access, while double underscores (e.g., __private) trigger to simulate privacy within class hierarchies. Modules serve as the primary unit for isolation, where code within the same module can freely access attributes of classes defined there, providing a coarse-grained alternative to fine-tuned . Descriptors offer advanced control for attribute access, but they do not replicate explicit cross-class permissions. In C#, the internal access modifier enables sharing of types and members across an entire assembly, functioning as a broad form of without class-specific . This allows any code within the same assembly to access internal elements, akin to a wide-reaching friend declaration, but lacks the precision of designating individual classes. For more targeted sharing, friend assemblies permit a specific external assembly to access internals of another, extending visibility beyond assembly boundaries while maintaining encapsulation elsewhere. Unlike C++, there is no mechanism for per-class , emphasizing assembly-level boundaries over object-level ones. Rust eschews friend declarations entirely, enforcing privacy through a module-based visibility system where items are private by default and must be explicitly marked public with the pub keyword to be accessible outside their module. Modules and crates provide hierarchical control, allowing associated functions to interact with struct fields while restricting external access to private components, thus promoting safe exposure without direct cross-type privileges. This design prioritizes compile-time checks on visibility paths over runtime or explicit grants, differing from C++'s permissive friendship model. C++'s explicit friend mechanism remains unique in permitting deliberate, granular cross-class access to private members, a feature that influenced subsequent languages seeking to balance encapsulation and collaboration. In the D programming language, for instance, this concept shaped the module system's design, where classes and functions within the same module implicitly gain access to each other's private members, eliminating the need for friend keywords while achieving similar selective visibility. This implicit approach simplifies syntax for C++ migrants, treating modules as trusted boundaries akin to a collective friendship group.

References

Add your contribution
Related Hubs
User Avatar
No comments yet.