C++系列(友元)

我们提到过C++中存在一种朋友关系,这种朋友关系如果体现在函数上,那么我们就称之为友元函数;如果体现在类上,我们就称之为友元类。

友元函数

对于我们定义函数的情形来说,一种情况是将函数定义为全局函数,另一种情况是将函数定义在一个类当中,使其成为类的一个成员函数。如果将全局函数声明为友元,则成为友元全局函数;如果将一个类的成员函数声明为另外一个类的友元函数,那么称该成员函数为友元成员函数。

友元全局函数

我们先来看一个例子

C++系列(友元)_第1张图片

我们定义了一个坐标类(Coordinate),那么,如果我们想要定义一个友元,怎么办呢?我们就要使用关键字:friend。要定义一个友元函数,就只需要将关键字friend加在函数声明的前面,最后加上分号即可,同时一定要传入当前这个类的一个对象或者是一个引用或者是指针,总之,能够通过这个函数能够访问到这个对象的私有的成员或者是受保护的成员(如上面的:friend void printXY(Coordinate &c);)。下面我们来看一看,对于Coordinate这个类来说,他的私有的成员有哪些??一个是m_iX代表的是横坐标,一个是m_iY代表的是纵坐标。下面我们来看一看我们提到的使用方法。

C++系列(友元)_第2张图片

在第一个方框中,我们写出的第一段程序叫做printXY函数,其是打印横纵坐标。在打印横纵坐标的时候吗,我们需要传入一个Coordinate的对象或者是引用(这儿传入的是引用),需要给大家指出的是传入引用或者指针,其传递效率更高,执行速度更快,所以在这提倡传入引用或者指针,而不提倡直接传入对象的方式。printXY函数在访问的时候,我们只是使用cout来打印一下横坐标和纵坐标。请大家注意我们使用的访问方法式使用这个对象去直接访问它的私有成员。如果我们没有将printXY声明为Coordinate的友元,那么如果我们这样写的话,编译器一定会报错。但是当前情况下,我们已经将printXY这个函数声明为Coordinate这个类的友元了,所以我们通过这样的直接访问形式是可以顺利编译通过的。当我们在mian函数当中去调用printXY函数的时候,我们需要先实例化一个Coordinate的对象,然后将这个对象传递进去,请大家注意,因为我们需要的参数是一个引用,所以我们传递的时候直接传入对象名就可以了,而不需要在对象名前面再加一个取地址符号(&)了。关于全局友元函数的定义和使用方法就先说这么多,后续通过代码实践进一步加深映像。


#ifndef COORDINATE_H
#define COORDINATE_H

class Coordinate
{
public:
    Coordinate(int x ,int y);
    Coordinate& operator-();//减号运算符重载
    Coordinate& operator+();//加号运算符重载
    Coordinate operator +(const Coordinate &coor);//二元运算符重载
    void  showxy();
    friend void printxy(Coordinate &c);//友元全局函数

private:
    int m_ix;
    int m_iy;
};

#endif // COORDINATE_H

#include 
#include "coordinate.h"
#include "time.h"
#include "match.h"

using namespace std;

void printxy(Coordinate &c)//友元全局函数
{
 cout<<"friend m_ix=="<


友元成员函数

我们还是通过一个例子来说明问题

C++系列(友元)_第3张图片

在这我们还是以Coordinate这个类为例。定义的时候仍然使用关键字friend。请大家注意后面的写法,我们使用的函数仍然叫做printXY,但是此时的printXY函数并不是一个全局函数,而是一个成员函数,其是Circle这个类中的成员函数。所以,我们要将Circle中的成员函数printXY声明为Coordinate这个类的友元,那么,我们就需要将Circle这个类写出来,然后加上(::)再接printXY,这样就可以将一个类的成员函数声明为另外一个类的友元了。

C++系列(友元)_第4张图片

在main函数当中,我们先实例化了一个Coordinate类的对象coor,然后又实例化了一个Circle类的对象circle。在Circle类中的printXY的实现方法与前面所讲到的全局函数的实现方法一样。通过这样的调用,我们可以发现,如果我们将Circle的printXY声明为Coordinate的友元,那么我们在printXY实现的时候就可以直接访问c这个对象下面的m_iX和m_iY,而m_iX和m_iY都是Coordinate下的私有成员,所以通过这样的行为就能够体现出友元给我们带来的方便。当然,友元给我们带来方便的同时,也给我们带来了一定的风险。当我们将Circle中的printXY这个函数声明为Coordinate这个类的友元函数之后,也就破坏了Coordinate这个类的封装性,此时对于数据的直接访问虽然是方便了,但是如果我们不小心改变了这个数据的值也不易擦觉,所以,风险和方便往往是一对相互矛盾。我们除非有特殊的需要,否则一般情况下不建议大家过度使用友元。

友元类

友元类的定义与友元函数的定义非常相似,也是使用关键字friend,后面跟一个类的类名即可。需要大家特别注意的是,如果我们要声明一个友元类的时候,需要在当前这个类的前面先声明这个类,如下所示:

C++系列(友元)_第5张图片

上面我们声明了Circle类为Coordinate类的友元类,而且在Coordinate类前面也声明了一下Circle类。

当我们将Circle这个类声明为Coordinate类的友元类之后,我们就可以在Circle这个类当中去定义一个Coordinate的对象了,并且可以通过这个对象任意访问Coordinate这个类当中的私有的数据成员和成员函数(在上面的例子中,我们只定义了数据成员,而没有定义成员函数)。

我们来看一下实际定义Circle类的时候是如何做的?

C++系列(友元)_第6张图片

我们看到,实际定义Circle的时候,我们就是在访问限定符private的下面定义了一个Coordinate的对象m_coor,在任何Circle当中的成员函数中,都可以通过这个对象来访问Coordinate中私有的数据成员或者成员函数。

关于友元的注意事项

  • 友元关系不可以传递(比如:B是A的朋友,C是B的朋友,但C未必就是A的朋友)
  • 友元关系的单向性(比如:A是B的朋友,B不一定就是A的朋友,所以在声明有缘的时候,一定要搞清楚到底A是B的友元,还是B是A的友元)
  • 友元声明的形式及数量不受限制(可以既有友元函数也有友元类,而且声明数量也不受限制)

注意:

友元只是封装的一种补充,其并不是一个很好的语法。也就是说,是不得已而为之所用的语法,如果在前期设计巧妙的话,实际上是可以避开友元的。这就是说,友元的使用破坏了封装性,使得类的封装性看上去更差,从而也体现了一种定向暴露的思想(我把谁当做朋友,也就相当于我把数据定向的暴露给谁了)。

#ifndef MATCH_H
#define MATCH_H
#include "time.h"
#include 
using namespace std;

class Time;//提前声明class Time


class Match//Match为Time的朋友需要引用Time,如上提前声明class Time
{
public:
    Match();
    void printT(Time &t);
    void STime(Time &t);


};

#endif // MATCH_H

#ifndef TIME_H
#define TIME_H
#include "match.h"


class Time
{
public:
    Time();
     //friend void printT(Time &t);//友元成员函数(不能访问私有成员????)
    friend class Match;//Match为Time的友元类,故Time对于Match是没有权限的
    void showTime();

private:

    int x;
    int y;
};

#endif // TIME_H


#include 
#include "coordinate.h"
#include "time.h"
#include "match.h"

using namespace std;

void printxy(Coordinate &c)//友元全局函数
{
 cout<<"friend m_ix=="<



你可能感兴趣的:(VS2015,+,Qt5.7,高级编程,Linux,&,C/C++)