Just as a class can grant private access to a non-member function via friend functions, it can also grant private access to an entire class. This can be beneficial if there is a coupling between two classes.
Introducing friend classes
Consider a Rectangle class and a Square class.
A square can be a rectangle if the area of both objects are the same. To determine if a Square can be transformed into a rectangle, we can establish a friendship between the two.
Therefore, Square can inspect the private space of Rectangle to determine whether the rectangle can be represented as a square. This still keeps Rectangle‘s private space private.
#include <iostream>
class Square;
class Rectangle {
private:
int m_width {};
int m_height {};
public:
Rectangle(int width, int height)
: m_width { width }
, m_height { height }
{
}
friend class Square;
};
class Square {
private:
int m_side {};
public:
explicit Square(int side)
: m_side { side }
{
}
bool hasSameAreaAs(const Rectangle& rectangle) const{
int squareArea { m_side * m_side };
int rectangleArea { rectangle.m_width * rectangle.m_height };
return squareArea == rectangleArea;
}
static bool canRepresent(const Rectangle& rectangle){
return rectangle.m_width == rectangle.m_height;
}
};
int main(){
Rectangle displayPanel { 10, 10 };
Rectangle poster { 8, 12 };
Square tile { 10 };
std::cout << std::boolalpha;
std::cout << "Display panel can be a square: "
<< Square::canRepresent(displayPanel) << '\n';
std::cout << "Poster can be a square: "
<< Square::canRepresent(poster) << '\n';
std::cout << "Tile has same area as display panel: "
<< tile.hasSameAreaAs(displayPanel) << '\n';
}
Terminal output:
Display panel can be a square: true
Poster can be a square: false
Tile has same area as display panel: true
Rectangle has established a friendship with Square: friend class Square;
Therefore, Square now has access to Rectangle‘s private space. It accesses it in Square::hasSameAreaAs()
Friendship is not bidirectional
This friendship only goes one way:
friend class Square;
It means:
Squarecan access private members ofRectangle.
It does not mean:
Rectanglecan access private members ofSquare.
Friendship is not transitive
Friendship also does not pass through a chain. If class A is a friend of B, and B is a friend of C, that does not mean A is a friend of C.
Friendship is not inherited
If class A makes B a friend, classes derived from B are not friends of A. Friendship in C++ requires explicit access.
Refactoring the friend class example
In the first example, every member function of Squarereceives access to every private member of Rectangle.
This is overkill.
Let’s refactor the program to make Rectangle use a friend method.
#include <iostream>
class Rectangle;
class Square {
private:
int m_side {};
public:
explicit Square(int side)
: m_side { side }
{
}
bool hasSameAreaAs(const Rectangle& rectangle) const;
};
class Rectangle {
private:
int m_width {};
int m_height {};
public:
Rectangle(int width, int height)
: m_width { width }
, m_height { height }
{
}
friend bool Square::hasSameAreaAs(const Rectangle& rectangle) const;
};
bool Square::hasSameAreaAs(const Rectangle& rectangle) const
{
int squareArea { m_side * m_side };
int rectangleArea { rectangle.m_width * rectangle.m_height };
return squareArea == rectangleArea;
}
int main()
{
Rectangle poster { 8, 12 };
Square tile { 10 };
std::cout << std::boolalpha;
std::cout << tile.hasSameAreaAs(poster) << '\n';
}
Terminal output:
false
Now only this member function is a friend:
friend bool Square::hasSameAreaAs(const Rectangle& rectangle) const;
The rest of Square does not automatically receive access to Rectangle’s private data. In this implementation, we enforce encapsulation.
Use friendship sparingly
Friendship is not bad :(, but it should be intentional.
Use it when two types are tightly coupled and the public interface would become awkward or artificial without privileged access.
Avoid it when a normal public member function would solve the problem cleanly.
For example, Rectangle could expose:
int area() const;
bool isSquare() const;
Then Square may not need friendship at all.
It’s a cruel world.