Friend Classes and Functions

An introduction to the friend keyword, which allows classes to give other objects and functions enhanced access to its members
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

DreamShaper_v7_nurse_Sidecut_hair_modest_clothes_fully_clothed_2.jpg
Ryan McCombe
Ryan McCombe
Posted

In our previous lessons covering classes and structs, we introduced access specifiers which allowed us to declare how accessible the variables and functions of our class are:

  • public functions and members are accessible from anywhere
  • protected functions and members are accessible only to the same class, or from a child class
  • private functions and members are accessible only within the same class

This allowed us to simplify our class interface by hiding away the inner workings of our class. We could use the public access specifier to make only a small number of our functions accessible to the outside world. Creating a smaller public interface in this way makes our classes friendlier to use and easier to maintain.

However, this can be inflexible. There are many cases where we want more granular control.

For example, our class might have some data and methods that are of particular interest to another specific class or function. But we don’t want to make them public for everyone - that’s too heavy-handed and can eliminate the benefits of encapsulation.

Instead, we can solve these problems by having our class befriend the other class, using the friend keyword.

Friend Functions

A class can declare free functions (ie, a function that is not part of another class) as friends using the friend keyword, followed by the function signature. In the following example, MyClass befriends three different functions:

class MyClass {
  friend void SomeFunction();
  friend bool AnotherFunction(int);
  friend void OneMore(int, float);
};

More generally, the syntax is

friend return_type function_name(argument_list);

Any function our class identifies as a friend will have access to everything in our class, including protected and private members.

Here is a complete example, where our class befriends the void LogCalls(MyClass) function, giving it access to the private Calls variable:

#include <iostream>

// Forward Declarations
class MyClass;
void LogCalls(MyClass);

class MyClass {
  friend void LogCalls(MyClass);

public:
  void operator()(){ ++Calls; }

private:
  int Calls{0};
};

void LogCalls(MyClass Functor){
  std::cout << "That functor has been called "
    << Functor.Calls << " times";
}

int main(){
  MyClass Functor;
  Functor();
  Functor();
  Functor();
  LogCalls(Functor);
}
That functor has been called 3 times

Friend Properties

Before we discuss some other possibilities of the friend keyword, it’s worth noting some of its properties:

A friend statement can appear anywhere in the class

It doesn’t matter where the friend statement appears within our class. It can come at the start, the end, or anywhere in between - the effect is the same.

A friend statement is not subject to access specifiers

It doesn’t matter if a friend statement appears in a public, protected, or private part of the class - the effect is the same

Befriending is not mutual

If A declares B a friend, that does not give A additional access to B.

For that, B needs to explicitly declare A a friend too.

Befriending is not transitive

If A declares B a friend, and B declares C a friend, that does not give C additional access to A.

For that, A needs to explicitly declare C a friend.

Befriending is not inherited

If Child inherits from Parent, and Parent declares SomeClass a friend, that does not grant SomeClass access to Child.

For that, Child needs to explicitly declare SomeClass a friend.

Friend Classes

Our class can befriend another class, using the friend class keyword:

class MyClass {
  friend class Analytics;
};

This gives any member of Analytics access to any member of MyClass:

#include <iostream>

class MyClass {
  friend class Analytics;

public:
  void operator()(){ ++Calls; }

private:
  int Calls{0};
};

class Analytics {
public:
  static void LogCalls(MyClass Functor){
    std::cout << "That functor has been called "
      << Functor.Calls << " times";
  }
};

int main(){
  MyClass Functor;
  Functor();
  Functor();
  Functor();
  Analytics::LogCalls(Functor);
}
That functor has been called 3 times

Friend Class Functions

Where we want to befriend a class function, the syntax is similar to what we used to befriend a free function. We simply have to add the class name and the scope resolution operator to our friend statement:

// Forward Declaration
class MyClass;

class Analytics {
public:
  static void LogCalls(MyClass Functor);
};

class MyClass {
  friend void Analytics::LogCalls(MyClass);
};

The following code shows a complete example of this. Note, we’ve had to move the definition of Analytics above the definition of MyClass in this example. We’ve then also had to move the definition of Analytics::LogCalls below the definition of MyClass.

This is to ensure everything is appropriately defined before it is used. In real programs, this is rarely an issue, as our classes would be in separate header files:

#include <iostream>

class MyClass;

class Analytics {
public:
  static void LogCalls(MyClass Functor);
};

class MyClass {
  friend void Analytics::LogCalls(MyClass);

public:
  void operator()(){ ++Calls; }

private:
  int Calls{0};
};

void Analytics::LogCalls(MyClass Functor){
  std::cout << "That functor has been called "
    << Functor.Calls << " times";
}

int main(){
  MyClass Functor;
  Functor();
  Functor();
  Functor();
  Analytics::LogCalls(Functor);
}
That functor has been called 3 times

Friend Template Classes and Functions

We can befriend class and function templates by declaring template parameters in the normal way

class MyClass {
  // Befriending a class template
  template <typename>
  friend class Analytics;

  // Befriending a function template
  template <typename T>
  friend void LogCalls(T);
};

In this example, every instantiation of the template, including specializations of the template, is befriended.

If we want to befriend just a specific instantiation of a template class or function, we can provide the template parameters within our friend statement. This requires that the templates have already been declared:

// Forward Declarations
template <typename T>
class Analytics;

template <typename T>
void LogCalls(T);

class MyClass {
  friend class Analytics<MyClass>; 
  friend void LogCalls<MyClass>(MyClass); 
};

A complete, working example using both a class template and a function template:

#include <iostream>

class MyClass {
  template <typename>
  friend class Analytics;

  template <typename T>
  friend void LogCalls(T);

public:
  void operator()(){ ++Calls; }

private:
  int Calls{0};
};

template <typename T>
class Analytics {
public:
  static void LogCalls(T Functor){
    std::cout << "That functor has been called "
      << Functor.Calls << " times\n";
  }
};

template <typename T>
void LogCalls(T Functor){
  std::cout << "That functor has been called "
    << Functor.Calls << " times";
}

int main(){
  MyClass Functor;
  Functor();
  Functor();
  Functor();
  Analytics<MyClass>::LogCalls(Functor);
  LogCalls(Functor);
}
That functor has been called 3 times
That functor has been called 3 times

Was this lesson useful?

Edit History

  • — First Published

Ryan McCombe
Ryan McCombe
Posted
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

7a.jpg
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access!

This course includes:

  • 106 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

Mutable Class Members

An introduction to the mutable keyword, which gives us more flexibility when working with const objects.
Character Concept Art
Contact|Privacy Policy|Terms of Use
Copyright © 2023 - All Rights Reserved