C++中的友元

我们知道任何对象都必须先定义,才可以使用,对于类来说也是如此。对于类来说,只有定义类才能根据类的成员确定存储空间。一个类在给定的源文件中只能定义一次;在多个文件中定义一个类,那么每个类的定义必须是完全相同的。我们一般把类的定义放在头文件中保证每个类的文件以相同方式定义类,使用头文件保护符(header guard)保证即使头文件被包含多次,类定义也只出现一次。

然而一个类也可以只声明而不定义,并以受局限的方式使用,例如

Class Student;

这是一个类的前向声明,而暂时没有定义,此时称他为一个不完全类型,即已经知道student是一个类型,但暂时不知道包含哪些成员。不完全类型可以以如下几种方式使用:

  1. 用于定义指向该类型的指针以及引用。
  2. 声明(不是定义)使用该类型作为形参或返回类型的函数。

链表的定义就是第一种情况的典型代表:

typedef int ElemType; 

typedef class Node 

{ 

    ElemType data; 

    Node *next; 

}LNode, *LinkedList;

Node尚未完整定义,但是在类体内有成员next,它是一个只想Node对象的指针。只要类名一出现就可以认为类已经声明。

接下来我们说一说友元。

友元出现的目的是为了允许特定非成员函数访问一个类的私有成员,同时仍然阻止一般的访问。友元机制虽然在一定程度上破坏了封装性,但是某些情况下,比如重载操作符,某些操作是不可能成为类成员的(我们稍后说明),但仍要访问类的私有数据成员,这个时候就需要声明为它们为类的友元。

我们考虑下面的例子,如果你读过C++ Primer,应该是对此例子不陌生的。

假设我们有一个类Screen用于显示窗口,而我们还有一个类Window_Mgr,用于统一管理一组Screen,那么可能它需要管理Screen的内部数据,这个时候我们可以让Window_Mgr成为Screen的友元。

class Screen 

{ 

    friend class Window_Msg; 

};

但是有时我我们并不需要让整个类,而只需要类中某个方法成为友元。这个时候可以只在类的友元声明中声明该方法。例如上面说的的Window_Mgr如果有一个relocate方法(index是Screen内部的一个类型别名)。

Window_Mgr& Window_Mgr::relocate(Screen::index r, Screen::index c,Screen& s) 

{ 

    s.height+=r; 

    s.width+=c; 

    return *this; 

}

那么可以只将此方法声明为友元:

class Screen 

{ 

    friend Window_Mgr& Window_Mgr::relocate(Screen::index r, Screen::index c,Screen& s); 

};

重点:友元之间的相互依赖关系。

我们考虑下面的例子:

有两个类,Student和Teacher,其中学生类Student有一个private类型的score,和一个public方法getScore获取分数。

而教师类Teacher有一个changeScore方法,原型为:voidTeacher::changeScore(Student &student,intnewScore),需要修改学生数据,显然参数应该为学生类型的引用和一个新的分数。

那么,我们应该如何声明和定义这两个类(在同一文件中)?

你可能会说是

class Teacher 

{ 

public: 

    void changeScore(Student &student,int newScore) 

    { 

        student.score=newScore; 

    } 

}; 

class Student 

{ 

public: 

    Student(int sc):score(sc){} 

    friend void Teacher::changeScore(Student &student,int newScore); 

    int getScore(){return score;} 

private: 

    int score; 

};

但是问题很明显,在changeScore中使用了Student的引用形参,而在此之前没有关于Student的任何信息,所以一定出错。那么我们加上

classStudent;

这样的声明呢?已然不对,为什么呢?因为在类体力我们使用了Studentscore成员,而前面我们已经说过,在不完全类型里面不能引用其成员。

那么如何才是正确的呢?答案是:

class Student; 



class Teacher 

{ 

public: 

    void changeScore(Student &student,int newScore); 

}; 



class Student 

{ 

public: 

    Student(int sc):score(sc){} 

    friend void Teacher::changeScore(Student &student,int newScore); 

    int getScore(){return score;} 

private: 

    int score; 

}; 









void Teacher::changeScore(Student &student,int newScore) 

{ 

    student.score=newScore; 

}

让我们来分析一下:

首先我们声明Student,是为了在Teacher类中changeScore方法可以使用其引用类型的形参,而该方法的定义需要放到Student类定义之后,因为此时才知道Student类型有那些成员。

此时代码看起来很乱,所以这也是我们为什么要把类的定义放在不同文件中(当然还有其他原因)。

当我们把Student和Teacher类的定义放在各自的头文件并把成员函数的实现放在各自的源文件中,问题轻松解决。(主要相互包含以及使用#pragmaonce)。
原文链接: https://www.cnblogs.com/DrizzleX/archive/2012/05/02/2478401.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/49169

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月9日 上午12:50
下一篇 2023年2月9日 上午12:51

相关推荐