7.3.4. Friendship Revisited

Our Sales_data class defined three ordinary nonmember functions as friends (§ 7.2.1, p. 269). A class can also make another class its friend or it can declare specific member functions of another (previously defined) class as friends. In addition, a friend function can be defined inside the class body. Such functions are implicitly inline.

Friendship between Classes

As an example of class friendship, our Window_mgr class (§ 7.3.1, p. 274) will have members that will need access to the internal data of the Screen objects it manages. For example, let’s assume that we want to add a member, named clear to Window_mgr that will reset the contents of a particular Screen to all blanks. To do this job, clear needs to access the private data members of Screen. To allow this access, Screen can designate Window_mgr as its friend:

class Screen {
    // Window_mgr members can access the private parts of class Screen
    friend class Window_mgr;
    // ... rest of the Screen class
};

The member functions of a friend class can access all the members, including the nonpublic members, of the class granting friendship. Now that Window_mgr is a friend of Screen, we can write the clear member of Window_mgr as follows:

class Window_mgr {
public:
    // location ID for each screen on the window
    using ScreenIndex = std::vector<Screen>::size_type;
    // reset the Screen at the given position to all blanks
    void clear(ScreenIndex);
private:
    std::vector<Screen> screens{Screen(24, 80, ' ')};
};
void Window_mgr::clear(ScreenIndex i)
{
    // s is a reference to the Screen we want to clear
    Screen &s = screens[i];
    // reset the contents of that Screen to all blanks
    s.contents = string(s.height * s.width, ' '),
}

We start by defining s as a reference to the Screen at position i in the screens vector. We then use the height and width members of that Screen to compute anew string that has the appropriate number of blank characters. We assign that string of blanks to the contents member.

If clear were not a friend of Screen, this code would not compile. The clear function would not be allowed to use the height width, or contents members of Screen. Because Screen grants friendship to Window_mgr, all the members of Screen are accessible to the functions in Window_mgr.

It is important to understand that friendship is not transitive. That is, if class Window_mgr has its own friends, those friends have no special access to Screen.


Image Note

Each class controls which classes or functions are its friends.


Making A Member Function a Friend

Rather than making the entire Window_mgr class a friend, Screen can instead specify that only the clear member is allowed access. When we declare a member function to be a friend, we must specify the class of which that function is a member:

class Screen {
    // Window_mgr::clear must have been declared before class Screen
    friend void Window_mgr::clear(ScreenIndex);
    // ... rest of the Screen class
};

Making a member function a friend requires careful structuring of our programs to accommodate interdependencies among the declarations and definitions. In this example, we must order our program as follows:

• First, define the Window_mgr class, which declares, but cannot define, clear. Screen must be declared before clear can use the members of Screen.

• Next, define class Screen, including a friend declaration for clear.

• Finally, define clear, which can now refer to the members in Screen.

Overloaded Functions and Friendship

Although overloaded functions share a common name, they are still different functions. Therefore, a class must declare as a friend each function in a set of overloaded functions that it wishes to make a friend:

// overloaded storeOn functions
extern std::ostream& storeOn(std::ostream &, Screen &);
extern BitMap& storeOn(BitMap &, Screen &);
class Screen {
    // ostream version of storeOn may access the private parts of Screen objects
    friend std::ostream& storeOn(std::ostream &, Screen &);
    // . . .
};

Class Screen makes the version of storeOn that takes an ostream& its friend. The version that takes a BitMap& has no special access to Screen.

Friend Declarations and Scope

Classes and nonmember functions need not have been declared before they are used in a friend declaration. When a name first appears in a friend declaration, that name is implicitly assumed to be part of the surrounding scope. However, the friend itself is not actually declared in that scope (§ 7.2.1, p. 270).

Even if we define the function inside the class, we must still provide a declaration outside of the class itself to make that function visible. A declaration must exist even if we only call the friend from members of the friendship granting class:

struct X {
    friend void f() { /* friend function can be defined in the class body   */ }
    X() { f(); } // error: no declaration for f
    void g();
    void h();
};
void X::g() { return f(); } // error: f hasn't been declared
void f();                   // declares the function defined inside X
void X::h() { return f(); } // ok: declaration for f is now in scope

It is important to understand that a friend declaration affects access but is not a declaration in an ordinary sense.


Image Note

Remember, some compilers do not enforce the lookup rules for friends (§ 7.2.1, p. 270).



Exercises Section 7.3.4

Exercise 7.32: Define your own versions of Screen and Window_mgr in which clear is a member of Window_mgr and a friend of Screen.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.154.64