I l@ve RuBoard Previous Section Next Section

4.7 Collaboration Sometimes Requires Friendship

The nonmember instance of operator*() directly accesses both the private member _elems of the Triangular class and the private member function check_integrity() of the Triangular_iterator class.



inline int operator*( const Triangular_iterator &rhs ) 


{ 


    rhs.check_integrity(); 


    return Triangular::_elems[ rhs.index() ]; 


} 

Why does this compile? A class can designate functions and classes as friends. The friends of a class can access the private members of that class in the same way as the member functions of that class. For this instance of operator*() to compile, it must be a friend to both the Triangular and the Triangular_iterator class:



class Triangular { 


   friend int operator*( const Triangular_iterator &rhs ); 


   // ... 


}; 





class Triangular_iterator { 


   friend int operator*( const Triangular_iterator &rhs ); 


   // ... 


}; 

We declare a function a friend to a class by prefacing its prototype with the friend keyword. The declaration can appear anywhere in the class definition. It is not affected by the private or public access levels of the class. (To make multiple instances of an overloaded function friends of a class, we must explicitly list each instance.)

The Triangular_iterator instance of operator*(), as well as its check_integrity() member function, both directly access the private members of the Triangular class. We can declare both instances as friends of the Triangular class:



class Triangular { 


   friend int Triangular_iterator::operator*(); 


   friend void Triangular_iterator::check_integrity(); 


   // ... 


}; 

For this to compile successfully, the Triangular_iterator class definition must be provided before its two member functions are declared as friends. Otherwise, the compiler hasn't enough information to confirm the correct prototype of the two member functions or even that one or both are member functions of the class.

Alternatively, we can grant friendship to the class itself, in turn conferring friendship on all the member functions of that class. For example,



class Triangular { 


   // confers friendship on all the 


   // member functions of Triangular_iterator 


   friend class Triangular_iterator; 





   // ... 


}; 

This form of class friendship does not require that the definition of the class be seen before the friend declaration.

Friendship, however, is not always required. For example, consider the definition of check_integrity():



inline void Triangular_iterator:: 


check_integrity() 


{ 


    if ( _index < Triangular::_max_elems ) 


         throw iterator_overflow(); 





    if ( _index > Triangular::_elems.size() ) 


         Triangular::gen_elements( _index ); 


} 

If the Triangular class provides a public access function to _max_elems and a public function to return the current size of _elems, then check_integrity() does not need the proffered friendship. For example,



class Triangular { 


public: 


    static int elem_size() { return _elems.size(); } 


    static int max_elems() { return _max_elems;    } 


    // ... 


}; 





// no longer needs to be a friend 


inline void Triangular_iterator:: 


check_integrity() 


{ 


    if ( _index < Triangular::max_elems() ) 


         throw iterator_overflow(); 





    // grow vector if necessary ... 


    if ( _index > Triangular::elems_size() ) 


         Triangular::gen_elements( _index ); 


} 

Friendship is generally required for performance reasons, such as the multiplication of a Point and Matrix in a nonmember operator function. For a simple read or write of a data member, an inline public access function is usually an adequate alternative to friendship.

Here is a small program to exercise our iterator class:



int main() 


{ 


   Triangular tri( 20 ); 


   Triangular::iterator it = tri.begin(); 


   Triangular::iterator end_it = tri.end(); 





   cout << "Triangular Series of " << tri.length() << " elements\n"; 


   while ( it != end_it ) 


   { 


      cout << *it << ' '; 


      ++it; 


   } 





   cout << endl; 


} 

When compiled and executed, this program generates the following output:



Triangular Series of 20 elements 


3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 210 231 
    I l@ve RuBoard Previous Section Next Section