代码改变世界

白话C++系列(30) -- 友元类

2016-06-28 20:43  Keiven_LY  阅读(508)  评论(0编辑  收藏  举报

友元类

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

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

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

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

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

关于友元的注意事项

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

注意:

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

友元类代码实践

题目描述:

/*   ****************************************************   */

/*  友元类

/*   ****************************************************   */

程序框架:

从程序框架上来看,跟之前所讲的友元函数是一样的,也定义了两个类:一个是Time类,一个是Match类。但是这里所讲的内容跟之前有些不同,我们一起来看一看。

首先在Time类(Time.h文件)中,我们在private下面又定义了一个成员函数printTime(),在这个成员函数下面是三个数据成员:时、分、秒。

头文件(Time.h

#ifndef TIME_H
#define TIME_H

class Time
{    
public:
    Time(int hour, int min, int sec);
private:
    void printTime(); 
    int m_iHour;
    int m_iMinute;
    int m_iSecond;
};

#endif

我们再来看一看printTime函数是如何定义的,跳到Time.cpp中

源程序(Time.cpp

#include"Time.h"
#include<iostream>
using namespace std;

Time::Time(inthour, intmin, intsec)
{
    m_iHour = hour;
    m_iMinute = min;
    m_iSecond = sec;
}
void Time::printTime()
{
    cout << m_iHour <<""<< m_iMinute <<""<< m_iSecond <<""<< endl;
}

此外,我们在Match.h中又加了一个成员函数testTime(),并且在Match.cpp中做了实现,这个实现是通过数据成员m_tTimer来调用printTime以及时、分。秒数据成员。我们可以看到,无论是成员函数还是数据成员都是写在了Time类中的private下面的。我们再来看一下Match.h中的数据成员,其是一个Time的数据成员m_tTimer(计时)。如下所示:

头文件(Match.h

#ifndef MATCH_H
#define MATCH_H

#include"Time.h"

class Match
{
public:
    Match(int hour, int min, int sec);
    void testTime();
private:
    Time m_tTimer;
};

#endif

源程序(Match.cpp

#include"Match.h"
#include<iostream>
using namespace std;

Match::Match(int hour, int min, int sec):m_tTimer(hour, min, sec)//通过初始化列表来实例化m_tTimer的三个参数
{
   // to do
}

void Match::testTime()
{
    m_tTimer.printTime();
    cout << m_tTimer.m_iHour <<":"<< m_tTimer.m_iMinute <<":"<< m_tTimer.m_iSecond << endl;
}

主调程序(demo.cpp

#include<iostream>
#include"stdlib.h"
#include"Time.h"
#include"Match.h"

using namespace std;

int main()
{
    Match m(6,30,56);
    m.testTime();

    system("pause");
    return 0;
}

在这里我们实例化了一个Match对象m,并传入了三个参数:时分秒,接着通过对象m来调用testTime()函数。

程序到这我们并没有在Time.h中声明Match为其友元类,所以当我们按F7的时候,可以看到:

也就是说,此时我们所调用的printTime函数或者数据成员,都因为其是private的访问形式而无法被调用。

所以此时,我们需要到Time.h文件中,将Match类声明为Time类的友元类,如下:

#ifndef TIME_H
#define TIME_H

class Match;//因为要声明友元,必须在此前要先声明一下
class Time
{
    friend Match; //声明Match类为Time类的友元类
public:
    Time(int hour, int min, int sec);
private:
    void printTime(); 
    int m_iHour;
    int m_iMinute;
    int m_iSecond;
};

#endif

现在我们再来看一看运行结果:

从打印结果可以看到,一个是“6时30分56秒”,一个是“6:30:56”,之所以能打印出中而两行结果,,这与Match中的testTime函数的实现有关系。我们看到第一行“m_tTimer.printTime()”这一行打印出来的就是“6时30分56秒”,第二行通过cout的方式就是打印出的“6:30:56”。