第17章 友元
第17章 和谐友好、随意使用的成员——友元
家里的好东西、贵重物品一般是不轻易借给别人使用的,如电视机、计算机等,但好朋友例外。C++也是一样,在类里声明为保护或私有的成员都是不外借的,外部不能访问,也就是外部不能对这些保护、私有成员(包括数据成员和函数成员)进行赋值、修改、显示等所有操作。如果外部需要频繁地对这些保护、私有成员进行操作,而且还要兼顾保留一些其他性能时,就可以考虑设置友元函数(就像好朋友一样),这些友元函数就可以与保护或私有成员和谐友好地相处,即可以随意使用了。
怎样才能成为 C++ 的好朋友?又怎样体现和谐友好呢?和 C++ 做朋友吧,这样就能分享到其中的快乐了。
17.1 运用友元计算全班所有科目的总平均分的例题详解
【例 17-1】计算全班所有科目的总平均分,要求使用运算符重载、友元函数编程。
- 用心分析人想通,机随人行写提纲
大致与例 14-1 的设计思路相同,只不过把 display 成员函数声明为友元函数了。
- 对号入座写文章,头脑运行改文章
#include<iostream>
using namespace std;
class Ave
{
protected:
double ave;
int s;
public:
Ave(double ave1,int s1);
Ave operator+(Ave a);
Ave avevalue(Ave b);
friend void display(Ave c);//把 display 成员函数声明为友元函数
};
Ave::Ave(double ave1,int s1)
{
ave=avel;
s=s1;
}
}
Ave Ave::operator+(Ave a)
{
ave=ave+a.ave;
Ave result(ave,s);
return result;
}
Ave Ave ::avevalue(Ave b)
{
ave=b.ave/b.s;
Ave result(ave,s);
return result;
}
void display(Ave c)//定义友元函数,其格式参考表17-1及17.3节
{
cout<<c.ave<<endl;
}
void main()
{
Ave e1(80.6,2);
Ave e2(87.9,2.0);
Ave e3(0,2.0);
e3=e1+e2;
e3=e3.avevalue(e3);
display(e3);
}
- C++帮助且裁判,人定胜机凯歌扬上机实验记录如表17-1所示。
| 出错现象 | 出错原因 | 解决办法 |
| 'Ace' : is not a class or namespace name | 因为在 display 函数前加了 Ace | 去掉 Ace,改为 Ave,此错纠正 |
| 'display' : is not a member of 'Ave' | display 不是 Ave 的成员,那么它作为友元函数,就不属于 Ave 类了 | 去掉 display 前的 Ave,此错纠正 |
运行结果:
84.25
结果正确。
17.2 勤奋实践,总结评分
17.2.1 习题
- 在商场购物时,只要在计算机上输入商品的名称,计算机就能输出该商品的现有数量和单价以供选择,请用类编写一个程序实现该操作。
- 有几个不同类型的用户共享一台打印机,使用时必须先调用一个检测程序,该检测程序用于检测打印机是否有人在使用,请用一个友元函数编写这个检测程序。
3.某商场通过运行商品自动销售情况统计软件来预计进货量,该软件能显示商品名称、销售时间、商品的销售量和商品的库存量,试编写程序实现。
- 把加法运算符重载为友元函数,该函数做复数相加的工作。
- 将第7章习题的第5题用友元函数编写程序。
- 将第7章习题的第6题用友元函数编写程序。
- 将第7章习题的第4题用友元函数编写程序。
8.试用友元函数编写一道程序,可以查询某校在校学生人数、教职工人数、学校面积、所设专业等学校的公开情况。
- 将第8章习题的第8题用友元函数编写程序。
- 将第7章习题的第10题用友元函数编写程序。
17.2.2 思考题
1.输入以2009年1月1日为基准的天数,编写一个程序计算出所输入天数对应的2009年的具体日期。
2.设想生活、工作等场合中的一些浏览、查询或打印公共数据的操作,用友元函数编写程序来实现。
17.2.3 总结评分,继续提高
浏览、体验了例题、习题和思考题之后,说说自己的体会和收获,并认真总结,按表 17-2 给自己评判一下可以得多少分。
| 成绩 | A | B | C | D | 不足的原因 | 改进的方法 | 备注 |
| C++知识 | 完全理解和会用 | 基本理解和会用 | 肤浅理解和会用 | 基本不理解和不会用 | |||
| 程序原创(独立编程)的比例(%) | ≥90 | 80~89 | 60~79 | <60 |
不论得分多少,大家都要总结经验,扬长避短,勤奋努力,不断提高。
最后浏览一下本章的第4节,可以先考考自己,然后再浏览,以便使重点知识掌握得更牢固。
17.3 友元知识
怎样才能与 C++ 成为好朋友,好朋友之间又如何相处呢?且往下观赏体验。
17.3.1 在什么情况下用到友元
当需要频繁使用类中的保护或私有成员,想提高程序的运行效率,使得编程更方便,但又对这些成员的某些操作还保留其保护或私有的权限(即对类的整体操作性和程序的封装性不会有完全性的破坏)时,可以考虑用友元函数。
例如,在商场货物的类中,把商品的名称、型号、规格、数量、单价等设置为私有或保护成员,顾客查询商品的操作用友元函数,而将对商品的其他操作设计为函数成员,这样就基本上符合上述应用友元的原则了。
注意:不要滥用友元函数。有的人在程序编写或调试中碰到困难时,就乱用友元函数,虽然可能程序能运行了,但带来的副作用却可能让你得不偿失。
17.3.2 关于友元
首先了解一下友元,再看看友元怎样与 C++ 成为好朋友。
1. 什么是友元
友元是一种函数,它能访问类中的全部成员。
但友元并不是类的成员函数(即使放在类中声明),它是类的朋友,是友元函数。这很容易理解,就像一个人不能既是某个家庭中的成员,又是这个家庭的朋友。
但友元函数又不是普通函数,因为普通函数不能访问类中的所有成员。
2. 友元函数的声明及定义
(1) 友元函数的声明
反元函数的声明与成员函数的声明方法基本一样,只是在原有的成员函数前加上关键字friend,且友元函数只能放在类的内部声明,在类中的任何区域声明均可(如保护、私有、公有区域均可)。
【例 17-2】请声明一个显示类 Ave 的数据的友元函数。
friend void display(Ave c);//把显示(display)成员函数声明为友元函数
(2) 友元函数的定义
反元函数的定义与普通函数的定义方法一样,二者均放在类的外部定义。但反元函数的定义一般放在声明它的类成员函数的外部定义区。
【例 17-3】请定义一个显示类 Ave 的数据的友元函数。
void display(Ave c)//定义显示类 Ave 的数据的友元函数,并定义了类的对象 c 作为形参
{
cout<<c.ave<<endl;//显示对象 c 的数据成员 ave 的值
}
注意:友元函数的定义的第一行就不要写 friend、类名及类的作用域区分符 “::” 了,用普通函数的格式书写即可。
3. 友元函数的使用方法
当声明和定义了友元函数之后,就可以使用友元函数了。
其使用方法与普通函数的使用方法一样,必须加上对象名来引用类中的成员。
17.3.3 如何运用友元
要在满足要求、又不违反友元函数使用原则的前提下,设计好在何处使用友元函数。使用时按照友元函数的书写格式声明和定义,使用方法与以前所讲的普通函数的使用方法一样,因为友元还是函数,只不过可以友好地使用类中的所有成员罢了。切记:不要滥用友元函数,程序中不一定非要有友元函数。
要根据 C++ 的规则用友元函数与 C++ 交朋友,还要成为好朋友。编写、调试和运行程序时都要看看友元函数有没有触犯 C++ 交朋友的规则,有没有伤害程序的整体性能等。如果有则加以修改,直到它们和谐友好地成为好朋友。
17.4 友元的亮点
1. 在什么情况下用友元的亮点
当需要频繁使用类中的保护或私有成员,想提高程序的运行效率,使得编程更方便,但又对这些成员的某些操作还保留其保护或私有的权限(即对类的整体操作性和程序的封装性不会有完全性的破坏)时,可以考虑用友元函数。
2. 友元具体知识的亮点
1)友元是一种函数,它能访问类中的全部成员。
2)友元不是类的成员函数。
3)友元函数也不是普通函数。
4)友元函数的声明与成员函数的声明方法基本一样,只是在原有的成员函数前加上关键字 friend,且友元函数只能放在类的内部声明。友元函数的定义与普通函数的定义方法一样,二者均放在类的外部定义。但友元函数的定义一般放在声明它的类成员函数的外部定义区。
5)使用友元函数的方法是:与普通函数的使用方法一样,必须加上对象名来引用类中的成员。
3. 运用友元的亮点
1)要在满足要求、又不违反友元函数使用原则的前提下,设计好在何处使用友元函数。
2)使用时按照友元函数的书写格式声明和定义,使用方法与以前所讲的普通函数一样。