CLOSE
Updated on 07 Aug, 202528 mins read 124 views

Friendship is magic

Inside the body of a class, a friend declaration (using the friend keyword) can be used to tell the compiler that some other class or function is now a friend. In C++, a friend is a class or function (member or non-member) that has been granted full access to the private or protected members of another class. In this way, a class can selectively give other classes or functions full access to their members without impacting anything else.

Friendship is always granted by the class whose members will be accessed (not by the class or function desiring access). Between access controls and granting friendship, a class always retains the ability to control who can access its members.

Why Do We Need It?

Suppose we have a class with private data members and we have to access those data members from outside of the class.

#include <iostream>

using namespace std;

class Base{
    private:
        int var;
    public:
        Base(int val) {
        	var = val;
        }
};

int main() {
    Base base(7);
    
    cout << base.var;
}

When we compile this program we get the following error:

main.cpp:17:18: error: 'var' is a private member of 'Base'
    cout << base.var;
                 ^
main.cpp:7:13: note: declared private here
        int var;
            ^
1 error generated.

It means we are trying to access the private member of the Base class which doesn't allow the outside access due to encapsulation.

So how do we access it:

One way is to make use of getter, setter: Well these are the member functions of the class which allows us to either get the value or set the value of the private members variable of the class. As these will be the actual members of the class and in private access specifier class members can access the private members.

#include <iostream>

using namespace std;

class Base{
    private:
        int var;
    public:
        Base(int val) {
        	var = val;
        }
    
    // Getter
    int getVar() {
        return var; 
    }
    
    // Setter
    void setVar(int val) {
        var = val;
    }
};

int main() {
    Base base(7);
    
    cout <<"var = " << base.getVar();
}

// Output: var = 7

Well in this solution we defined two function getter and setter to manipulate private member variables of the class. These function are still members of the class.

Apart from it we have another solution of accessing the private members of the class from outside the class is friend function.

A friend function is a function that is not a member of a class but is granted access to the private and protected members of that class.

Friend non-member functions

A friend function is a function (member or non-member) that can access the private and protected members of a class as though it were a member of that class. In all other regards, a friend function is a normal function.

Why Use a Friend Function?

  • To access private data of a class from a non-member function.
  • Useful when you want a function (or another class) to interact closely with the internals of a class, without making it a member.

Syntax:

class ClassName {
	// ...
	
	friend ReturnType FunctionName (Arguments);
};

This tells the compiler that FunctionName is allowed to access private and protected members of ClassName.

Definition outside the class:

ReturnType FunctionName(Arguments) {
	// Function code
}
#include <iostream>
using namespace std;

class Box {
private:
    double width;

public:
    Box(double w) : width(w) {}

    // Declare friend function
    friend void printWidth(Box b);
};

// Define friend function
void printWidth(Box b) {
    // Can access private members of Box
    cout << "Width: " << b.width << endl;
}

int main() {
    Box b1(10.5);
    printWidth(b1);  // OK: friend function has access
    return 0;
}

// Output: Width: 10.5

Let's take a look on example:

#include <iostream>

using namespace std;

class Base{
    private:
        int var;
    public:
        Base(int val) {
        	var = val;
        }
    
    friend int friendFunc(Base base);
};

int friendFunc(Base base) {
    return base.var;
}

int main() {
    Base base(7);
    
    cout <<"var = " << friendFunc(base);
}

// Output: var = 7
#include <iostream>

class Accumulator
{
private:
    int m_value { 0 };

public:
    void add(int value) { m_value += value; }

    // Here is the friend declaration that makes non-member function void print(const Accumulator& accumulator) a friend of Accumulator
    friend void print(const Accumulator& accumulator);
};

void print(const Accumulator& accumulator)
{
    // Because print() is a friend of Accumulator
    // it can access the private members of Accumulator
    std::cout << accumulator.m_value;
}

int main()
{
    Accumulator acc{};
    acc.add(5); // add 5 to the accumulator

    print(acc); // call the print() non-member function

    return 0;
}

In this example, we have declared a non-member function named print() that takes an object of class Accumulator. Because print() is not a member of the Accumulator class, it would normally not be able to access private member m_value. However, the Accumulator class has a friend declaration making print(const Accumulator& accumulator) a friend.

Note that because print() is a non-member function (does not have an implicit object), we must explicitly pass an Accumulator object to print() to work with.

Defining a friend non-member inside a class

Much like member functions can be defined inside a class if desired, friend non-member functions can also be defined inside a class. The following examples defines friend non-member function print() inside the Accumulator class:

#include <iostream>

class Accumulator
{
private:
    int m_value { 0 };

public:
    void add(int value) { m_value += value; }

    // Friend functions defined inside a class are non-member functions
    friend void print(const Accumulator& accumulator)
    {
        // Because print() is a friend of Accumulator
        // it can access the private members of Accumulator
        std::cout << accumulator.m_value;
    }
};

int main()
{
    Accumulator acc{};
    acc.add(5); // add 5 to the accumulator

    print(acc); // call the print() non-member function

    return 0;
}

Although you might assume that because print() is defined inside Accumulator, that makes print() a member of Accumulator, this is not the case. Because print() is defined as a friend, it is instead treated as a non-member function (as if it had been defined outside Accumulator).

Syntactically preferring a friend non-member function

We prefer to use a non-member function over a member function.

#include <iostream>

class Value
{
private:
    int m_value{};

public:
    explicit Value(int v): m_value { v }  { }

    bool isEqualToMember(const Value& v) const;
    friend bool isEqualToNonmember(const Value& v1, const Value& v2);
};

bool Value::isEqualToMember(const Value& v) const
{
    return m_value == v.m_value;
}

bool isEqualToNonmember(const Value& v1, const Value& v2)
{
    return v1.m_value == v2.m_value;
}

int main()
{
    Value v1 { 5 };
    Value v2 { 6 };

    std::cout << v1.isEqualToMember(v2) << '\n';
    std::cout << isEqualToNonmember(v1, v2) << '\n';

    return 0;
}

In this example, we have defined two similar functions that check whether two Value objects are equal. isEqualToMember() is a member function, and isEqualToNonmember() is a non-member function.

In isEqualToMember(), we are passing one object implicitly and other explicitly. The implementation of the function reflects this, and we have to mentally reconcile that m_value belongs to the implicit object whereas v.m_value belongs to the explicit parameter.

In isEqualToNonmember(), both objects are passed explicitly. This leads to better parallelism in the implementation of the function, as the m_value member is always explicitly prefixed with an explicit parameter.

Friend Function vs Member Function

FeatureMember FunctionFriend Function
Belongs to classYesNo
Access to private dataYesYes (if declared friend)
Called with objectYes (obj.func())No (func(obj))
Useful whenFunction acts on object's internal stateFunction needs to access internals but isn't conceptually part of the object

Leave a comment

Your email address will not be published. Required fields are marked *