Friend functions in C++
Non-member functions in C++ that have been given special access by a class to its private and protected members are known as friend functions. Through this approach, some functions are able to “breach the walls” of encapsulation, which typically limit access to secret data to functions that are members of that class.
Key Characteristics of Friend Functions
- Access Rights: Similar to a member function, a friend function has complete access privileges to all private and protected members of the class that defines it as a friend.
- Non-Member: Although it has access capabilities, a friend function is not a class member.
- Scope: A friend function does not fall inside the class’s purview because it is not a member.
- No this Pointer: Member functions are the only ones with access to this pointer; friend functions do not. Consequently, the friend function usually requires explicit parameters to be given in order to access objects whose secret or protected members need to be accessible.
- Declaration: To be a friend, a function must have its prototype before the friend keyword in the class declaration. Since friend declarations are unaffected by access control specifiers, this declaration can be found anywhere in the class specification, such as in the private or public sections.
- Definition: The friend keyword and the scope resolution operator are not used in the actual definition of the friend function, which is written outside the class scope (::).
- Invocation: Like standard non-member functions, friend functions are called without the use of an object or the dot operator (.).
When are Friend Functions Useful?
Friend functions are useful in certain situations where a non-member function’s direct access to private members offers substantial advantages or is required for logical syntax:
Operator Overloading: Using buddy functions to overload operators particularly binary operators like operator<< (output stream) or operator+—when the left-hand operand is not an object of the class type is one of their most popular applications. To access the private data of myObject, for instance, std::cout \< myObject; necessitates that operator\< be a non-member function.
- A buddy function for binary operators expressly accepts both operands as parameters, in contrast to a member operator that implicitly uses the object it is called on as the left operand.
- To modify an object, a buddy function requires one explicit parameter, usually a reference to the object, for unary operators (e.g., increment ++ or decrement –).
Interrelated Classes: Friends can be used as a “bridge” when a function wants to access the private information of objects of two or more different, unrelated classes. This makes it possible for many sorts of operations to function more cohesively.
Functional Notation: Some operations are more natural using friend functions (e.g., square(distance) instead than distance.square()).
Efficiency: Friends may give more efficient code than public accessor methods because they have less overhead. This benefit must be weighed against less encapsulation.
Limitations and Considerations
- Encapsulation Concerns: Buddy functions are versatile, but their broad use may weaken the data encapsulation principle, making code harder to maintain and perhaps introducing inconsistencies if the class structure changes. As such, their use ought to be limited.
- No Inheritance: From derived classes, friendship is not inherited. Functions declared as friends by base classes are not automatically become friends by derived classes. Friends of a derived class also do not have special access to the private members of the base class.
- Not Virtual: There is no way to declare a friend function as virtual.
- No Storage-Class Specifiers: External or static storage-class specifiers cannot be used to declare friend functions.
- Specific Operators: Operator=, operator(), operator[], and operator-> are among the operators that must be non-static member functions in order to be overloaded using a buddy function.
- Visibility: In general, access is granted by a friend declaration. In order to provide user visibility and callability, a buddy function frequently needs to be declared independently outside of the class definition in the surrounding scope.
Friend Classes and Template Friends
- Friend Classes: It is possible to define a class as a buddy of another class in place of specific functions. The private and protected members of the class that grants friendship are thus accessible to all member functions of the friend class. Friend classes, like friend functions, are not inheritable and should be utilised with caution.
- Template Friends: Other templates or non-templates can be declared as friends by class templates. Friendship may be extended to all instances of a template or restricted to particular instances. You can even make the type parameter of a template a friend (friend Type;, for example). In the case of template functions, the buddy is usually identified as a template function by the <> that follows the function name.
Code Example
The C++ program that follows shows how to use the mean friend function, which is a straightforward way to access the private data elements a and b of the Sample class:
#include <iostream> // For input/output operations
// Define the Sample class
class Sample
{
private:
int a; // Private data member
int b; // Private data member
public:
// Public member function to set values
void setvalue() { a = 25; b = 40; }
// Declare 'mean' as a friend function.
// This line grants the 'mean' function access to 'Sample's private members 'a' and 'b'.
// The 'friend' keyword precedes the function prototype.
friend float mean(Sample s);
};
// Define the friend function 'mean'.
// It is defined outside the class scope and does not use the scope resolution operator (::).
// It takes a 'Sample' object 's' as an argument because it doesn't have a 'this' pointer.
float mean(Sample s)
{
// 'mean' can directly access 's.a' and 's.b' because it was declared as a friend of 'Sample'.
return (static_cast<float>(s.a + s.b) / 2.0);
}
int main()
{
Sample x; // Create an object of class Sample
x.setvalue(); // Call a public member function to initialize private data
// Call the friend function 'mean' directly, like a regular non-member function.
// No object or dot operator (x.) is used to call 'mean'.
std::cout << "Mean value: " << mean(x) << std::endl;
return 0;
}
Output
Mean value: 32.5