Team LiB
Previous Section Next Section

12.5. Friends

12.5. 友元

In some cases, it is convenient to let specific nonmember functions access the private members of a class while still preventing general access. For example, over-loaded operators, such as the input or output operators, often need access to the private data members of a class. For reasons we'll see in Chapter 14 these operators might not be members of the class. Yet, even if they are not members of the class, they are "part of the interface" to the class.

在某些情况下,允许特定的非成员函数访问一个类的私有成员,同时仍然阻止一般的访问,这是很方便做到的。例如,被重载的操作符,如输入或输出操作符,经常需要访问类的私有数据成员。这些操作符不可能为类的成员,具体原因参见第十四章。然而,尽管不是类的成员,它们仍是类的“接口的组成部分”。

The friend mechanism allows a class to grant access to its nonpublic members to specified functions or classes. A friend declaration begins with the keyword friend. It may appear only within a class definition. Friend declarations may appear anywhere in the class: Friends are not members of the class granting friendship, and so they are not affected by the access control of the section in which they are declared.

友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类。友元的声明以关键字 friend 开始。它只能出现在类定义的内部。友元声明可以出现在类中的任何地方:友元不是授予友元关系的那个类的成员,所以它们不受声明出现部分的访问控制影响。

Ordinarily it is a good idea to group friend declarations together either at the beginning or end of the class definition.

通常,将友元声明成组地放在类定义的开始或结尾是个好主意。



Friendship: An Example

友元关系:一个例子

Imagine that in addition to the Screen class we had a window manager that manages a group of Screens on a given display. That class logically might need access to the internal data of the Screen objects it manages. Assuming that Window_Mgr is the name of the window-management class, Screen could let Window_Mgr access its members as follows:

想像一下,除了 Screen 类之外,还有一个窗口管理器,管理给定显示器上的一组 Screen。窗口管理类在逻辑上可能需要访问由其管理的 Screen 对象的内部数据。假定 Window_Mgr 是该窗口管理类的名字,Screen 应该允许 Window_Mgr 像下面这样访问其成员:

     class Screen {
         // Window_Mgr members can access private parts of class Screen
         friend class Window_Mgr;
         // ...restofthe Screen class
     };

The members of Window_Mgr can refer directly to the private members of Screen. For example, Window_Mgr might have a function to relocate a Screen:

Window_Mgr 的成员可以直接引用 Screen 的私有成员。例如,Window_Mgr 可以有一个函数来重定位一个 Screen

     Window_Mgr&
     Window_Mgr::relocate(Screen::index r, Screen::index c,
                          Screen& s)
     {
          // ok to refer to height and width
          s.height += r;
          s.width += c;

          return *this;
     }

In absence of the friend declaration, this code would be in error: It would not be allowed to use the height and width members of its parameter named s. Because Screen grants friendship to Window_Mgr, all the members of Screen are accessible to the functions in Window_Mgr.

缺少友元声明时,这段代码将会出错:将不允许使用形参 sheightwidth 成员。因为 Screen 将友元关系授予 Window_Mgr,所以,Window_Mgr 中的函数都可以访问 Screen 的所有成员。

A friend may be an ordinary, nonmember function, a member function of another previously defined class, or an entire class. In making a class a friend, all the member functions of the friend class are given access to the nonpublic members of the class granting friendship.

友元可以是普通的非成员函数,或前面定义的其他类的成员函数,或整个类。将一个类设为友元,友元类的所有成员函数都可以访问授予友元关系的那个类的非公有成员。

Making Another Class' Member Function a Friend

使其他类的成员函数成为友元

Instead of making the entire Window_Mgr class a friend, Screen could have specified that only the relocate member was allowed access:

如果不是将整个 Window_Mgr 类设为友元,Screen 就可以指定只允许 relocate 成员访问:

     class Screen {
         // Window_Mgrmust be defined before class Screen
         friend Window_Mgr&
             Window_Mgr::relocate(Window_Mgr::index,
                                  Window_Mgr::index,
                                  Screen&);
         // ...restofthe Screen class
     };

When we declare a member function to be a friend, the name of the function must be qualified by the name of the class of which it is a member.

当我们将成员函数声明为友元时,函数名必须用该函数所属的类名字加以限定。

Friend Declarations and Scope

友元声明与作用域

Interdependencies among friend declarations and the definitions of the friends can require some care in order to structure the classes correctly. In the previous example, class Window_Mgr must have been defined. Otherwise, class Screen could not name a Window_Mgr function as a friend. However, the relocate function itself can't be defined until class Screen has been definedafter all, it was made a friend in order to access the members of class Screen.

为了正确地构造类,需要注意友元声明与友元定义之间的互相依赖。在前面的例子中,类 Window_Mgr 必须先定义。否则,Screen 类就不能将一个 Window_Mgr 函数指定为友元。然而,只有在定义类 Screen 之后,才能定义 relocate 函数——毕竟,它被设为友元是为了访问类 Screen 的成员。

More generally, to make a member function a friend, the class containing that member must have been defined. On the other hand, a class or nonmember function need not have been declared to be made a friend.

更一般地讲,必须先定义包含成员函数的类,才能将成员函数设为友元。另一方面,不必预先声明类和非成员函数来将它们设为友元。

A friend declaration introduces the named class or nonmember function into the surrounding scope. Moreover, a friend function may be defined inside the class. The scope of the function is exported to the scope enclosing the class definition.

友元声明将已命名的类或非成员函数引入到外围作用域中。此外,友元函数可以在类的内部定义,该函数的作用域扩展到包围该类定义的作用域。



Class names and functions (definitions or declarations) introduced in a friend can be used as if they had been previously declared:

用友元引入的类名和函数(定义或声明),可以像预先声明的一样使用:

     class X {
         friend class Y;
         friend void f() { /* ok to define friend function in the class body */ }
     };
     class Z {
         Y *ymem; // ok: declaration for class Y introduced by friend in X
         void g() { return ::f(); } // ok: declaration of f introduced by X
     };

Overloaded Functions and Friendship

重载函数与友元关系

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 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.

Screen 将接受一个 ostream&storeOn 版本设为自己的友元。接受一个 BitMap& 的版本对 Screen 没有特殊访问权。

Exercises Section 12.5

Exercise 12.32:

What is a friend function? A friend class?

什么是友元函数?什么是友元类?

Exercise 12.33:

When are friends useful? Discuss the pros and cons of using friends.

什么时候友元是有用的?讨论使用友元的优缺点。

Exercise 12.34:

Define a nonmember function that adds two Sales_item objects.

定义一个增加两个 Sales_item 对象的非成员函数。

Exercise 12.35:

Define a nonmember function that reads an istream and stores what it reads into a Sales_item.

定义一个非成员函数,读取一个 istream 并将读入的内容存储到一个 Sales_item 中。


Team LiB
Previous Section Next Section