类3(友元)

摘要:
如果我们希望类的用户调用友元函数,那么我们必须在友元声明之外声明一个函数:1#include<iostream>2usingspacestd;34classgel{5/friendvoid{}//Friend函数可以在内部定义。然而,这似乎没有用。如果使用了成员函数,它应该使用哪种朋友声明//朋友函数声明只声明访问权限,不能被视为

一、友元介绍

我们知道,类的成员函数可以访问同类的其他成员函数,包括公有、私有和保护成员。而类的外部函数只能访问类的公有成员。

友元是一种允许非类成员函数访问类的非公有成员的一种机制。
可以把一个函数指定为类的友元,也可以把整个类指定为另一个类的友元。

友元函数
友元类

二、友元函数
友元函数在类作用域外定义,但它需要在类体中进行说明
为了与该类的成员函数加以区别,定义的方式是在类中用关键字friend说明该函数,格式如下:

friend  类型 友元函数名(参数表);
友元的作用在于提高程序的运行效率

友元函数注意事项:
1、
友元函数不是类的成员函数,在函数体中访问对象的成员,必须用对象名加运算符“.”加对象成员名。但友元函数可以访问类中的所有成员(公有的、私有的、保护的),一般函数只能访问类中的公有成员。

2、友元函数不受类中的访问权限关键字限制,可以把它放在类的公有、私有、保护部分,但结果一样。

3、某类的友元函数的作用域并非该类作用域。如果该友元函数是另一类的成员函数,则其作用域为另一类的作用域,否则与一般函数相同。

4、友元函数破坏了面向对象程序设计类的封装性,所以友元函数如不是必须使用,则尽可能少用。或者用其他手段保证封装性。

就如我们在上一篇博客中的 Sales_data 类,如果将其中的数据成员修改成 private,则还需要将 add,read,print 这三个函数声明成 Sales_data 类的友元函数,因为我们在这三个函数中直接访问了 Sales_data 类的私有数据成员:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class Sales_data{
 5 
 6 //友元声明
 7 friend Sales_data add(const Sales_data&, const Sales_data&);
 8 friend std::istream &read(std::istream&, Sales_data&);
 9 friend std::ostream &print(std::ostream&, const Sales_data&);
10     
11     //数据成员
12 private:
13     std::string book_no;
14     unsigned units_sold = 1;
15     double revenue = 1.0;
16 
17 public:
18     Sales_data() = default;//不接受任何实参,默认构造函数
19     Sales_data(const std::string &s): book_no(s){}//除了book_no外其他成员将被编译器赋予默认值
20     Sales_data(const std::string &s, unsigned n, double p): book_no(s), units_sold(n), revenue(p * n){}
21     Sales_data(std::istream&);
22 
23     std::string isbn() const {
24         return book_no;
25         // return this->book_no;//等价语句
26     }
27     Sales_data& combine(const Sales_data&);
28     double avg_price() const;
29 };
30 
31 // Sales_data的非成员函数声明
32 Sales_data add(const Sales_data&, const Sales_data&);
33 std::ostream &print(std::ostream&, const Sales_data&);
34 std::istream &read(std::istream&, Sales_data&);
35 
36 Sales_data::Sales_data(std::istream &is){
37     read(is, *this);//read 函数的作用是从 is 中读取一条信息然后存入 this 中
38 }
39 
40 double Sales_data::avg_price() const{
41     if(units_sold) return revenue / units_sold;
42     return 0;
43 }
44 
45 Sales_data& Sales_data::combine(const Sales_data &rhs){
46     units_sold += rhs.units_sold;
47     revenue += rhs.revenue;
48     return *this;
49 }
50 
51 istream &read(istream &is, Sales_data &item){//从给定流中将数据读到给定的对象里
52     double price = 0;
53     is >> item.book_no >> item.units_sold >> price;
54     item.revenue = price * item.units_sold;
55     return is;
56 }
57 
58 ostream &print(ostream &os, const Sales_data &item){//将给定对象的内容打印到给定的流中
59     os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price();
60     //print 函数不负责换行。一般来说执行输出的函数应该尽量减少对格式的控制,这样可以确保由用户代码来决定是否换行
61     return os;
62 }
63 
64 Sales_data add(const Sales_data &lhs, const Sales_data &rhs){
65     Sales_data sum = lhs;//默认情况下拷贝的是数据成员
66     sum.combine(rhs);//把 rhs 的数据成员加到 sum 中
67     return sum;//返回 sum 的副本
68 }
69 
70 int main(void){
71     Sales_data total(cin), x;
72     // print(cout, x);
73     // read(cin, total);
74     while(read(cin, x)){
75         total = add(total, x);
76         print(cout, total);
77         cout << endl;
78     }
79     return 0;
80 }

还需要注意的一点是:友元的声明仅仅指定了访问的权限,而非一个通常意义上的函数声明。如果我们希望类的用户能够调用某个友元函数,那么我们就必须在友元声明之外再专门对函数进行一次声明:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class gel{
 5     // friend void f(gel&){}//友元函数可以定义在内的内部,然而好像这并没啥用,要是成员函数还用啥友元声明啊
 6     friend void f(gel&);//友元函数声明只是声明了访问权限,并不能当成一般的函数声明来用
 7     
 8 public:
 9     // gel(){//错误,f函数还没声明
10     //     f(*this);
11     // }
12     void lou();
13     void ting();
14 
15 private:
16     int x;
17 };
18 
19 // void gel::lou(){
20 //     f(*this);//错误,f函数还没声明
21 // }
22 
23 void f(gel &it);//声明f函数
24 
25 void gel::ting(){//正确
26     f(*this);
27 }
28 
29 void f(gel &it){
30     it.x += 1;
31 }
32 
33 int main(void){
34 
35 }

三、友元类
如果某类B的成员函数会频繁的存取另一个类A的数据成员, 而A的数据成员的Private/Protectd限制造成B存取的麻烦, B只能通过A的Public的成员函数进行间接存取
把B做成A类的友元类,即A类向B类开放其Private/Protectd内容, 让B直接存取

友元类:一个类可以作另一个类的友元
友元类的所有成员函数都是另一个类的友元函数

友元类的声明:
friend class 类名;

友元类注意事项:
1、友元关系是单向的
2、友元关系不能被传递
3、友元关系不能被继承

 1 #include <iostream>
 2 #include <vector>
 3 using namespace std;
 4 
 5 class Screen{
 6     
 7 friend class window_mgr;//将window_mgr类声明成Screen的友元类
 8 
 9 public:
10     typedef std::string::size_type pos;
11     Screen() = default;
12     //cursor为默认值0
13     Screen(pos ht, pos wd, char c): height(ht), width(wd), contents(ht * wd, c){};
14     char get() const{
15         return contents[cursor];
16     }
17 
18 private:
19     pos cursor = 0;
20     pos height = 0, width = 0;
21     std::string contents;
22 };
23 
24 
25 class window_mgr{
26     
27 public:
28     // window_mgr();
29     // ~window_mgr();
30     using screen_index = std::vector<Screen>::size_type;
31     void clear(screen_index);
32 
33 private:
34     std::vector<Screen> screen{Screen(24, 80, ' ')};
35     
36 };
37 
38 void window_mgr::clear(screen_index i){
39     Screen &s = screen[i];
40     s.contents = std::string(s.height * s.width, ' ');
41     //因为window_mgr是Screen类的友元类,所以可以直接访问Screen类中的private对象contents,height,width
42 }
43 
44 int main(void){
45 
46 }

上面的代码中,我们在 Screen 类中定义了 window_mgr 类为其友元类,所以 window_mgr 类的成员函数可以直接访问 Screen 类中的所有数据成员,包括 private 类型的成员。

除了令整个 window_mgr 类作为 Screen 类的友元之外,还可以只声明 window_mgr 类中的某个函数为 Screen 类的友元函数。当把一个成员函数声明成友元时,必须明确指出该成员函数属于哪个类:

 1 #include <iostream>
 2 #include <vector>
 3 using namespace std;
 4 
 5 class Screen;//下面的window_mgr需要用Screen作形参,所以我们需要先声明一下Screen类
 6 
 7 class window_mgr{
 8     
 9 public:
10     // window_mgr();
11     // ~window_mgr();
12     void clear(int i);
13 
14 private:
15     std::vector<Screen> screen;
16 
17 };
18 
19 class Screen{
20 
21 // friend class window_mgr;//将window_mgr类声明成Screen的友元类
22 friend void window_mgr::clear(int);//将window::clear定义成Screen的友元函数
23 // friend void clear(int);//没有指明clear所属的类,只样声明的clear友元函数和window_mgr类里的clear成员函数半毛钱关系都没有
24 
25 
26 public:
27     typedef std::string::size_type pos;
28     Screen() = default;
29     //cursor为默认值0
30     Screen(pos ht, pos wd, char c): height(ht), width(wd), contents(ht * wd, c){};
31     char get() const{
32         return contents[cursor];
33     }
34 
35 private:
36     pos cursor = 0;
37     pos height = 0, width = 0;
38     std::string contents;
39 };
40 
41 
42 void window_mgr::clear(int i){
43     Screen &s = screen[i];
44     s.contents = std::string(s.height * s.width, ' ');
45 }
46 
47 int main(void){
48 
49 }

然而我们发现想要令某个成员函数作为友元,我们必须自信组织程序的结构以满足声明和定义的彼此依赖关系。这相当麻烦,在整个例子中:

1.首先定义 window_mgr 类,其中声明 clear 函数,但是不能定义它。在使用 Screen 的成员之前还必须声明 Screen 类

2.接下来定义 Screen,包括 clear 的友元声明

3.定义 clear,此时它才可以使用 Screen 的成员

4.尽管重载函数的名字相同,但它们仍然是不同的函数。因此一个类想把一组重载函数声明成它的友元,它需要对这组函数中的每个函数都单独声明成友元函数

以上内容大部分摘自:http://www.jb51.net/article/41328.htm

免责声明:文章转载自《类3(友元)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇ZedGraph如何动态的加载曲线单链表选择排序下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

GeeTest 极验验证

 前台Html页面 <script src="http://libs.baidu.com/jquery/1.9.0/jquery.js"></script> <script src="http://static.geetest.com/static/tools/gt.js"></script>...

地图上面加柱状图组

地区地图阴影,加上单点柱状图组的展示 import echarts from 'echarts' import '../../../../map-json/jiangmen' const geoCoordMap = { 市区1: [113.0989, 22.81677], 市区2: [113.200601, 22.672211],...

08 在设备树里描述platform_device【转】

转自:https://blog.csdn.net/jklinux/article/details/78575281 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本文链接:https://blog.csdn.net/jklinux/article/details/78575281在设备树的dts文件...

继承QAbstractTableModel QStyledItemDelegate实现自定义表格,添加进度条和选中框。

由于项目要求,需要实现一个列表目录显示信息,并且需要实现每一项提供进度条和选项框功能,所以需要继承QAbstractTableModel和QStyledItemDelegate进行自定义。 -自定义数据 itemdata.h #ifndef ITEMDATA_H #define ITEMDATA_H #include <QMetaType> #...

c++之标准库iomanip

C++ 标准库之iomanip istream & istream::get(char *, int, char = ‘ ’); istream & istream::getline(char *, int, char = ‘ ’); 作用: 从文本中提取指定个数的字符串, 并在串数组末尾添加一个空字符. 区别: get() 不从流中提取终...

C#枚举(一)使用总结以及扩展类分享

0.介绍 枚举是一组命名常量,其基础类型为任意整型。 如果没有显式声明基础类型, 则为Int32 在实际开发过程中,枚举的使用可以让代码更加清晰且优雅。 最近在对枚举的使用进行了一些总结与整理,也发现了一些很有意思的知识盲区。 接下来先简单为大家介绍枚举在开发过程中的常用内容以及扩展类的分享。如果喜欢直接看代码的可以查看最后的样例源码。 1. 参考资料 官...