Friend Functions and Classes in C++

When learning about Classes in C++, you’ll often hear the terms Parent and Child. These terms are used to represent a relationship between two or more classes that allow special actions. For example, a child can access the non-private data of the Parent(s). Similar to Parents and Children, C++ has another relationship called “Friend” which can exist between Functions and Classes.

The C++ Friend relationship allows functions and classes to access all the data in a class, even the private data.

There are two types of Friend relationships in C++. The first exists between a Function and a Class, the second exists between two Classes.


C++ Friend Functions

In this section, we will be creating a function which can access any type of data from the Class that we create.

In the example below, the printDetails() function has been created. We want it to print out the data stored within the Data Class’s objects.

#include <iostream>
using namespace std;

class Data {
private:
    string name;
    int value;

public:
    Data(string s, int n) {
        name = s;
        value = n;
    }
};

void printDetails(Data &d) {
    cout << "Name: " << d.name << endl;
    cout << "Value: " << d.value << endl;
}

int main() {
    Data d("Apples", 10);

    printDetails(d);
}

The above code however, will return an error. Why will this not work? Because the private access level states that no-one is allowed to access the private members of the Data Class. The error you will get from the above code, is “data member is private”.

To get around this, we need to declare the function as a “friend” of the print() function. You need to have to correct name and parameters, else it will not work.

class Data {
    friend void printDetails(Data &d);
private:
    string name;
    int value;

public:
    Data(string s, int n) {
        name = s;
        value = n;
    }
};

If we now run the above code, we will get the following (correct) output.

Name: Apples
Value: 10

C++ Friend Classes

In this section, we will be allowing Class “A” to access the private and non-private data of Class “B”. Remember, Friendship is a one way relationship. If Class “A” declares Class “B” as a friend, Class B can access Class “A”s data. Until Class B also declares Class A as a friend however, Class A will not be able to access Class B’s data.

In the example below, Class A has declared Class B as a friend. This now means that the printDetails() function of Class B can access and display the private data of Class A.

#include <iostream>
using namespace std;


class A {
    friend class B;
private:
    string name;
    int value;
public:
    A(string s, int n) {
        name = s;
        value = n;
    }
};

class B {
private:
    int data;
public:
    printDetails(A &a) {
        cout << "Name: " << a.name << endl;
        cout << "Value: " << a.value << endl;
    }
};

int main() {
    A a("Bread", 20);
    B b;

    b.printDetails(a);
}

The output of the above code:

Name: Bread
Value: 20

Limiting Access

Instead of giving complete access to all member variables of Class A, we can instead choose which member variables of Class B are allowed to access Class A.

For this, things are going to get a little complex, atleast while we have everything in one file. The problem here, is that Class A needs to exist for Class B to use it in the printDetails() function parameters. Likewise, Class A needs Class B’s printDetails() function to be defined before it can be defined.

This brings in a bit of a dependency-issue, and requires the use of forward-declaration. First we declare Class A (declare, not define). Next we create Class B, and just define the printDetails() function. We can’t declare it yet as we need the variables of Class A to be declared, and so far, only Class A’s name is known to Class B.

Next we declare Class A, and also include the friend statement for the printDetails() function. This is possible even though the printDetails() has not defined (as it has been declared). Finally, right at the end we go ahead and define the printDetails() function of Class B.

#include <iostream>
using namespace std;

class A;

class B {
private:
    int data;
public:
    void printDetails(A &a);
};

class A {
    friend void B::printDetails(A &a);
private:
    string name;
    int value;
public:
    A(string s, int n) {
        name = s;
        value = n;
    }
};


void B::printDetails(A &a) {
    cout << "Name: " << a.name << endl;
    cout << "Value: " << a.value << endl;
}


int main() {
    A a("Bread", 5);
    B b;

    b.printDetails(a);
}

It’s a bit tricky, but makes sense what you think about the entire process individually.


Friend Classes across different Files

In C++ it’s very common (and standard practice) to have one class per file. Or rather, there is one header file and one .cpp file for each class. It can be a little confusing when setting up Friends across different files, so we’ve come up with a little example here.

We had to create 5 files in total, one for main, two for our first class and two for the second class. Each code section is labelled to help you identify them.

Ally.h
#include "Enemy.h"
#include <iostream>
using namespace std;
#pragma once

class Ally  {
    int magicPoints;
    int speed;
public:
    Ally(int mp, int spd) : magicPoints(mp), speed(spd) {}
    void print(Enemy e);
};
Ally.cpp
#include "Ally.h"

void Ally::print(Enemy e) {
    cout << e.attack << endl;
}

Enemy.h
#pragma once

class Enemy {
    friend class Ally;

    int attack;
    int health;
public:
    Enemy(int atk, int hp) : attack(atk), health(hp) {};

};
Enemy.cpp
#include "Enemy.h"
main
#include <iostream>
#include "Enemy.h"
#include "Ally.h"

using namespace std;

int main()
{
    Enemy e1(10, 4);
    Ally a1(3, 5);

    a1.print(e1);
    return 0;
}

This marks the end of the C++ Friend Functions and Classes Tutorial. Any suggestions or contributions for CodersLegacy are more than welcome. Questions regarding the tutorial content can be asked in the comments section below.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments