DEV Community

Cover image for Friends in C++
Rishabh Agarwal
Rishabh Agarwal

Posted on

Friends in C++

Friends in C++ are a way of breaking the encapsulation that classes provides. To use a real-life analogy, friends are like trusted acquaintances who are given access to your private information unlike strangers.

Let us consider a simple example…
Take the following definition of a class named MyClass.

class MyClass {
  int value;
public:
  MyClass(int value) : value(value) {}
};
Enter fullscreen mode Exit fullscreen mode

If we were to attempt writing a global display function for objects of this class, we would do something like the following.

// INCORRECT : Will Cause Compilation Failure
void display(MyClass& myClass) {
  // ERROR: myClass.value is private in MyClass!
  std :: cout << myClass.value << std :: endl; 
}
Enter fullscreen mode Exit fullscreen mode

But, as we are aware, the compilation for this function will fail because it tries to access private members of the MyClass class and since the display function is a non-member it can not do that.

Can we get our display function to work? Let us try to find a solution…

Approach 1: Why not mark the private data member value public? In this way, display method can easily access this data member.

Problem with Approach 1: Marking value public would loosen the encapsulation not only for display method but for all the other methods as well. This would not be a good approach and could lead to poor design.

Approach 2: So, if we can’t mark the value data member public, why not make display method a member of the MyClass?

Problem with Approach 2: The actual implementation of the display method depends on the use case and it might not be possible to cover all the different implementations in the class definition itself. It is better to leave implementation of display method to the user itself.

None of the standard approach seems to be solving our use case. The next question that we should be asking ourselves is — “Is there a way to selectively break the encapsulation for just the display method?”. The answer amazingly is a big YESSS.

As always… Friends to our rescue
A class in C++ can mark other functions and classes as friends. These functions and classes can then access private (along with public) members of the class where they are marked as friends. And this is how we selectively loosens the encapsulation.

Thus, to get our example working, we can mark the global display function as a friend of the class MyClass. Here is the updated class definition.

class MyClass {
  int value;
public:
  MyClass(int value) : value(value) {}
  friend void display(MyClass& myClass); // display method marked friend
};
Enter fullscreen mode Exit fullscreen mode

With this small change, the display method can now access the private members of the MyClass class. Isn’t it smooth? 🤯

Functions marked with the keyword friend are known as friend functions. Let us try to formally understand them.

Friend Functions

A friend function to a class has the following properties —

  • It has access to private and protected members of a class
  • Should have its prototype included within the class scope with the keyword friend.
  • It is not called with an invoking object of the class.
  • A friend function can be declared friend in multiple classes.

Also, a friend function can be —

  • Any global function
  • A member function of another class
  • A function template

Friend Classes

A friend class of a class —

  • has access to private and protected members of the class in addition to the public members
  • does not have its name qualified with the class scope
  • can be declared friend in more than one class Also, a friend class can be —
  • A class
  • A class template

We use the following syntax for marking another class (say OtherClass) as friend in our class.

class MyClass {
  ...
  friend class OtherClass;
  ...
};

Enter fullscreen mode Exit fullscreen mode

Properties of Friend(ship)

Following are some of the properties of friend in C++ —
☆ Friendship is neither commutative nor transitive.
[Commutativity] A is a friend of B, does not imply that B is a friend of A.
[Transitivity] Also, A being a friend of B and B being a friend of C does not imply that A is a friend of C.


This brings us to the end of yet another article on C++. Congratulations on reaching the end, I hoped you learned something new today.


A cross post from my Medium page!

Top comments (7)

Collapse
 
user64bit profile image
Arth • Edited

Great Article
I just had one question. If Class A is Friend Class of Class B. does that mean A is Inheriting B?
Because if It looks alike that.
But may be the difference will be, we cannot override the existing functionality.
tell me if I'm wrong :)

Collapse
 
pauljlucas profile image
Paul J. Lucas

Inheritance has nothing to do with friendship.

Collapse
 
user64bit profile image
Arth

forgot to add "kind of"

Thread Thread
 
pauljlucas profile image
Paul J. Lucas • Edited

Friendship isn’t "kind of" inheritance either. If A is a friend of B, the only thing that means is that, from B’s perspective, it’s like protected and private are public. That’s it. That’s all it means.

Even if all of B’s members were public, you still can't override functionality because friendship has nothing even remotely to do with inheritance or overriding.

Collapse
 
pauljlucas profile image
Paul J. Lucas

In your example, a simple "getter" function would eliminate the need for friend. As far as a "display" function goes, the canonical way is overloading operator<<.

std::ostream& operator<<( std::ostream &o, MyClass const &c ) {
    return o << c.getValue();
}
Enter fullscreen mode Exit fullscreen mode

Then you can do:

std::cout << myClass << std::endl;  // don't put spaces around ::
Enter fullscreen mode Exit fullscreen mode

In this case, no friend is needed assuming you provide getValue(). For more complicated cases, then you can make that operator<<() a friend. As long as you make the operator display the "stringified" representation of the class and nothing else, it's sufficient.

And you didn't sufficiently cover template friends.

Collapse
 
the_infinity profile image
Rishabh Agarwal

Thanks for your feedback, Paul. I appreciate your attention to detail, and I understand that a getter method or overloading the ostream operator may have been a more advanced or efficient option for the example provided. However, my intention is to provide relatable and accessible examples for beginners, which sometimes means that the examples used may not be the best use-case or most advanced implementation. Ultimately, my goal is to help people understand the topic at hand, and I hope that even with any imperfections, my blog posts can be a helpful resource for those learning the material.

Collapse
 
pauljlucas profile image
Info Comment hidden by post author - thread only accessible via permalink
Paul J. Lucas

Generally, you should never present suboptimal code as definitive (which happens by default unless you say otherwise). You give the impression that the code is the "right way" to do whatever. If you must present suboptimal code, say so along the lines of, "In production code, you'd never do it this way, but for the sake of simplicity, ...." You can the later present the optimal way and even why the suboptimal way was suboptimal, either in the same blog post or in a later blog post.

Some comments may only be visible to logged-in visitors. Sign in to view all comments. Some comments have been hidden by the post's author - find out more