Learn C++ - C++ Friend Functions






C++ provides another form of access: the friend.

Friends come in three varieties:

  • Friend functions
  • Friend classes
  • Friend member functions

By creating a friend function, you grant the function the access privileges that a class member function has.





Creating Friends

The first step is to place a prototype in the class declaration and prefix the declaration with the keyword friend:

friend Time operator*(double m, const Time & t);  // goes in class declaration 

This prototype has two implications:

operator*() function is declared in the class declaration, but it is not a member function. So it isn't invoked by using the membership operator and it has the same access rights as a member function.

The second step is to write the function definition.

Because it is not a member function, you don't use the ClassName:: qualifier.

You don't use the friend keyword in the definition.

The definition should look like this:

Time operator*(double m, const Time & t)  // friend not used in definition 
{ 
     Time result; 
     long totalminutes = t.hours * mult * 60 +t. minutes * mult; 
     result.hours = totalminutes / 60; 
     result.minutes = totalminutes % 60; 
     return result; 
} 




Syntax

To make a function a friend of a class, you must declare it as such within the class definition using the friend keyword.

It's the class that determines its friends; there's no way to make a function a friend of a class from outside the class definition. A friend function can be a global function or it can be a member of another class. By definition a function can't be a friend of the class of which it is a member so access specifiers don't apply to the friends of a class.

The following code shows how to implement a friend function in the Box class to compute the surface area of a Box object.

To make the function a friend, you must declare it as such within the Box class definition.

Here's a version that does that:


#include <iostream>   
#include <memory> 
// w  w w. ja  v a  2 s  . c  om
class Box { 
private: 
  double length; 
  double width; 
  double height; 
  
public: 
  // Constructors 
  Box(double lv = 1.0, double wv = 1.0, double hv = 1.0); 
    
  double volume();                                  // Function to calculate the volume of a box 
  
  friend double surfaceArea(const Box& aBox);       // Friend function for the surface area 
}; 
// Constructor definition 
Box::Box(double lv, double wv, double hv) : length(lv), width(wv), height(hv) { 
  std::cout << "Box constructor called." << std::endl; 
} 
 
// Function to calculate the volume of a box 
double Box::volume() 
{ 
  return length*width*height; 
} 
 
int main() 
{ 
  Box box1 {2.2, 1.1, 0.5};                              // An arbitrary box 
  Box box2;                                              // A default box 
  auto pBox3 = std::make_shared<Box>(15.0, 20.0, 8.0);   // Box on the heap 
  std::cout << "Volume of box1 = " << box1.volume() << std::endl; 
  std::cout << "Surface area of box1 = " << surfaceArea(box1) << std::endl; 
  
  std::cout << "Volume of box2 = "<< box2.volume() << std::endl; 
  std::cout << "Surface area of box2 = " << surfaceArea(box2) << std::endl; 
  
  std::cout << "Volume of box3 = " << pBox3->volume() << std::endl; 
  std::cout << "Surface area of box3 = " << surfaceArea(*pBox3) << std::endl; 
} 
  
// friend function to calculate the surface area of a Box object 
double surfaceArea(const Box& aBox) 
{ 
  return 2.0*(aBox.length*aBox.width + aBox.length*aBox.height +aBox.height*aBox.width); 
} 

The code above generates the following result.

Example

The First Version of Overloading <<.


#include <iostream> 
// w  ww . j  av  a2  s.c  o m
class Time { 
private: 
     int hours; 
     int minutes; 
public: 
     Time(); 
     Time(int h, int m = 0); 
    void AddMin(int m); 
    void AddHr(int h); 
    void Reset(int h = 0, int m = 0); 
     Time operator+(const Time & t) const; 
     Time operator-(const Time & t) const; 
     Time operator*(double n) const; 
     friend Time operator*(double m, const Time & t) 
         { return t * m; }   // inline definition 
     friend std::ostream & operator<<(std::ostream & os, const Time & t); 
}; 
Time::Time() 
{ 
     hours = minutes = 0; 
} 

Time::Time(int h, int m ) 
{ 
     hours = h; 
    minutes = m; 
} 

void Time::AddMin(int m) 
{ 
    minutes += m; 
     hours += minutes / 60; 
    minutes %= 60; 
} 
void Time::AddHr(int h) 
 { 
    hours += h; 
 } 

void Time::Reset(int h, int m) 
 { 
    hours = h; 
    minutes = m; 
 } 

Time Time::operator+(const Time & t) const 
 { 
    Time sum; 
     sum.minutes = minutes + t.minutes; 
     sum.hours = hours + t.hours + sum.minutes / 60; 
     sum.minutes %= 60; 
    return sum; 
 } 

Time Time::operator-(const Time & t) const 
 { 
    Time diff; 
     int tot1, tot2; 
    tot1 = t.minutes + 60 * t.hours; 
    tot2 = minutes + 60 * hours; 
    diff.minutes = (tot2 - tot1) % 60; 
    diff.hours = (tot2 - tot1) / 60; 
    return diff; 
 } 

Time Time::operator*(double mult) const 
 { 
    Time result; 
     long totalminutes = hours * mult * 60 + minutes * mult; 
    result.hours = totalminutes / 60; 
    result.minutes = totalminutes % 60; 
    return result; 
 } 

std::ostream & operator<<(std::ostream & os, const Time & t) 
 { 
    os << t.hours << " hours, " << t.minutes << " minutes"; 
    return os; 
 } 

int main() 
{ 
   using std::cout; 
   using std::endl; 
   Time dinner_time(3, 35); 
   Time movie_time(2, 48); 
   Time temp; 

   cout << dinner_time<<"; " << movie_time << endl; 
   temp = dinner_time + movie_time;     // operator+() 
   cout << "dinner_time + movie_time: " << temp << endl; 
   temp = dinner_time* 1.17;  // member operator*() 
   cout << "dinner_time * 1.17: " << temp << endl; 
   cout << "10.0 * movie_time: " << 10.0 * movie_time << endl; 

   return 0; 
} 

The code above generates the following result.

Example 2

The following code uses friend function to overload comparison operators.


#include <cstring>                 // string.h for some 
#include <iostream> 
using std::ostream; 
using std::istream; 
using std::cin; /*from   w w w .  j a v a2s. co  m*/
using std::cout; 

class String { 
private: 
     char * str;             // pointer to string 
     int len;                // length of string 
     static int num_strings; // number of objects 
     static const int CINLIM = 80;  // cin input limit 
public: 
// constructors and other methods 
     String(const char * s); // constructor 
     String();               // default constructor 
     String(const String &); // copy constructor 
     ~String();              // destructor 
     int length () const { return len; } 
// overloaded operator methods 
     String & operator=(const String &); 
     String & operator=(const char *); 
     char & operator[](int i); 
     const char & operator[](int i) const; 
// overloaded operator friends 
     friend bool operator<(const String &st, const String &st2); 
     friend bool operator>(const String &st1, const String &st2); 
     friend bool operator==(const String &st, const String &st2); 
     friend ostream & operator<<(ostream & os, const String & st); 
     friend istream & operator>>(istream & is, String & st); 
// static function 
     static int HowMany(); 
}; 
// initializing static class member 

int String::num_strings = 0; 

// static method 
int String::HowMany() 
 { 
    return num_strings; 
 } 

// class methods 
String::String(const char * s)     // construct String from C string 
 { 
    len = std::strlen(s);          // set size 
    str = new char[len + 1];       // allot storage 
    std::strcpy(str, s);           // initialize pointer 
    num_strings++;                 // set object count 
 } 

String::String()                   // default constructor 
 { 
    len = 4; 
    str = new char[1]; 
    str[0] = '\0';                 // default string 
    num_strings++; 
 } 

String::String(const String & st) 
 { 
    num_strings++;             // handle static member update 
    len = st.len;              // same length 
    str = new char [len + 1];  // allot space 
    std::strcpy(str, st.str);  // copy string to new location 
 } 

String::~String()                     // necessary destructor 
 { 
    --num_strings;                    // required 
    delete [] str;                    // required 
 } 

// overloaded operator methods 

// assign a String to a String 
String & String::operator=(const String & st) 
{ 
    if (this == &st) 
        return *this; 
    delete [] str; 
    len = st.len; 
    str = new char[len + 1]; 
    std::strcpy(str, st.str); 
    return *this; 
} 

    // assign a C string to a String 
String & String::operator=(const char * s) 
{ 
    delete [] str; 
    len = std::strlen(s); 
    str = new char[len + 1]; 
    std::strcpy(str, s); 
    return *this; 
} 

    // read-write char access for non-const String 
char & String::operator[](int i) 
{ 
    return str[i]; 
} 

    // read-only char access for const String 
const char & String::operator[](int i) const 
{ 
    return str[i]; 
} 

// overloaded operator friends 

bool operator<(const String &st1, const String &st2) 
{ 
    return (std::strcmp(st1.str, st2.str) < 0); 
} 

bool operator>(const String &st1, const String &st2) 
{ 
    return st2 < st1; 
} 

bool operator==(const String &st1, const String &st2) 
{ 
     return (std::strcmp(st1.str, st2.str) == 0); 
 } 

     // simple String output 
ostream & operator<<(ostream & os, const String & st) 
 { 
     os << st.str; 
     return os; 
 } 

     // quick and dirty String input 
istream & operator>>(istream & is, String & st) 
 { 
     char temp[String::CINLIM]; 
     is.get(temp, String::CINLIM); 
     if (is) 
         st = temp; 
     while (is && is.get() != '\n') 
         continue; 
     return is; 
 } 

const int SIZE = 10; 
const int MaxLen =81; 
using std::cout; 
using std::cin; 
using std::endl; 
int main() { 
     String name; 
     cout <<"Your name:\n>> "; 
     cin >> name; 

    cout << name << ", please enter up to " << SIZE 
         << " short sayings < empty line to quit>:\n"; 
    String sayings[SIZE];     // array of objects 
    char temp[MaxLen];          // temporary string storage 
    int i; 
    for (i = 0; i < SIZE; i++) { 
        cout << i+1 << ": "; 
        cin.get(temp, MaxLen); 
        while (cin && cin.get() != '\n') 
            continue; 
        if (!cin || temp[0] == '\0')    // empty line? 
            break;              // i not incremented 
        else 
            sayings[i] = temp;  // overloaded assignment 
    } 
    int total = i;              // total # of lines read 

    if ( total > 0) { 
        cout << "Here are your sayings:\n"; 
        for (i = 0; i < total; i++) 
            cout << sayings[i][0] << ": " << sayings[i] << endl; 

        int shortest = 0; 
        int first = 0; 
        for (i = 1; i < total; i++) { 
            if (sayings[i].length() < sayings[shortest].length()) 
                shortest = i; 
            if (sayings[i] < sayings[first]) 
                first = i; 
        } 
        cout << sayings[shortest] << endl;; 
        cout << sayings[first] << endl; 
        cout << String::HowMany() << " String objects. \n"; 
    } 
    return 0; 
} 

The code above generates the following result.