白话C++系列(30) -- 友元类
2016-06-28 20:43 Keiven_LY 阅读(521) 评论(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”。