IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++之友元:是朋友(friend)也是破坏者 -> 正文阅读

[C++知识库]C++之友元:是朋友(friend)也是破坏者

前言

hello。大家好,我们来继续分享关于C++的知识点,今天我们来分享一些关于友元的知识。也欢迎大家订阅我的免费专栏——以分号结尾的诗:C++,在这个专栏里,我会持续介绍关于C++的知识。闲言少叙,让我们开始吧。

1.友元的概念

1.1友元的基本概念

友元分为:友元函数和友元类和友元成员函数
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。

在这里插入图片描述

1.2友元的形式

1.友元函数

friend 函数名();

2.友元类

class 类名1; // 前置声明
class 类名2{
friend class 类名1; // 声明类1为类2的友元类,则在类1中就直接访问类2中的私有成员变量
……
}

3.友元成员函数

class class1;
class class2
{
	void f();
};
class class1
{
	friend void class2::f();
};

1.3为什么要有友元的存在

友元的存在真的必要吗?我们来可以下下面这个例子:
我们写了一个日期类的函数,将<<运算符重载,用以输出日期,但是重载没有采用友元(细心的朋友可能会发现在我们之前介绍运算符重载的文章中,这里的重载采用的是友元)。代码如下:


#include<iostream>
using namespace std;
class Date
{
	int _year;
	int _month;
	int _day;
public:
	Date(int year=0,int month=1,int day=1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	ostream&operator<<(ostream &_cout)
	{
		_cout << _year << "-" << _month << "-" << _day;
		return _cout;
	}
};
int main()
{
	Date d1(2021, 8, 9);
	cout << d1<<endl;
	return 0;
}

我们来看一下输出结果
在这里插入图片描述
好家伙,很迷惑,竟然报错了,这是为什么呢?
在这里插入图片描述
我们将程序做一个小小的修改,注意只是小小的修改哦,修改后代码如下;

#include<iostream>
using namespace std;
class Date
{
	int _year;
	int _month;
	int _day;
public:
	Date(int year = 0, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	ostream&operator<<(ostream &_cout)
	{
		_cout << _year << "-" << _month << "-" << _day;
		return _cout;
	}
};
int main()
{
	Date d1(2021, 8, 9);
	d1 << cout << endl;
	return 0;
}

我们只是将d1与cout换了一个位置,我们再来看输出结果:
在这里插入图片描述
咦?惊不惊喜意不意外?
在这里插入图片描述
为什么会这样呢?我们来分析一下:
我们尝试去重载operator<<,然后发现我们没办法将operator<<重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。
我们将其展开如下图,很明显,我们不可以改变操作数的顺序啊。
在这里插入图片描述
那么我们又该如何解决这个问题呢?我们可以使cout重新变回左操作数吗?
但是实际使用中cout需要是第一个形参对象,才能正常使用。所以我们要将operator<<重载成全局函数。但是这样的话,又会导致类外没办法访问成员,那么这里就需要友元来解决。
我们来看一下:

#include<iostream>
using namespace std;
class Date
{
	int _year;
	int _month;
	int _day;
public:
	Date(int year = 0, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	friend ostream&operator<<(ostream &_cout, const Date d);
};
ostream&operator<<(ostream &_cout,const Date d)
{
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}
int main()
{
	Date d1(2021, 8, 9);
    cout<<d1 << endl;
	return 0;
}

在这里插入图片描述
至此,我们已经解决了这个问题。
通过这个问题我们可以发现,在使用类的过程中,有的时候我们不得不打破类的限制,突破类的边界,来使问题得到更好的解决。
友元的存在便可以打破边界突破限制。就像一个人,人为什么需要朋友呢?一个人心中藏着自己的故事,自己的思想,自己的秘密。这些东西独属于他自己。但人又是不甘于寂寞的,所以,人需要一些真正的知心朋友,将自己的一些故事,一些思想,一些秘密分享给朋友。一起分享、一起承担。这样人才不会那么孤独,这样人生活地才会更加快乐。我们与朋友之间彼此保留着自己的隐私,同时又分享彼此的忧喜,这是不是和友元很像呢?
在这里插入图片描述

2.友元函数

2.1友元函数的概念

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。

我们在上面的那个日期类中就用到了友元函数,现在我们来实现cout和cin的重载,代码如下:

class Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);
friend istream& operator>>(istream& _cin, Date& d);
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
_cout<<d._year<<"-"<<d._month<<"-"<<d._day;
return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{
_cin>>d._year;
_cin>>d._month;
_cin>>d._day;
return _cin;
}

2.2注意

1.友元函数可访问类的私有和保护成员,但不是类的成员函数
2.友元函数不能用const修饰
3.友元函数可以在类定义的任何地方声明,不受类访问限定符限制
4.一个函数可以是多个类的友元函数
5.友元函数的调用与普通函数的调用和原理相同
这几点注意事项都很好理解,我们就不过多解释啦。

3.友元类

3.1友元类的概念

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。

怎么来理解这句话呢?我们来看一个友元类的例子:

class Date; // 前置声明
class Time
{
friend class Date; // 声明日期类为时间类的友元类,则
//在日期类中就直接访问Time类中的私有成员变量
public:
Time(int hour, int minute, int second)
: _hour(hour)
, _minute(minute)
, _second(second)
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
void SetTimeOfDate(int hour, int minute, int second)
{
// 直接访问时间类私有的成员变量
_t._hour = hour;
_t._minute = minute;
_t.second = second;
}
private:
int _year;
int _month;
int _day;
Time _t;
};

在上面这段代码中,日期类为时间类的友元,日期类可以直接访问时间类中所有的非公有成员。
友元类相对于友元函数将可访问的范围扩大了,友元函数是从类外可以访问类内的一个函数,而友元类是可以在另一个类中可以访问这个类的所有成员函数。
在这里插入图片描述

2.2注意

1.友元关系是单向的,不具有交换性。但是两个类可以互为友元。
比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
2.友元关系不能传递。如果B是A的友元,C是B的友元,则不能说明C时A的友元。
这两条也很好理解,我们可以继续沿用上面的例子。对于1,我们可以联想到一个很事实,就是你愿意分享给你的朋友的东西,你的朋友可能并不愿意分享给你。或者更残忍一点,你的朋友,并不会也把你当作朋友。对于2,一个很简单的道理,朋友的朋友不一定是你的朋友哦。还记得小时候你的那复杂的三角朋友关系吗?A不要你和B玩,B不要你和A玩,你却要和A、B玩,A与B却不相往来,哈哈哈,小朋友间的三角友谊很有趣的哦。
在这里插入图片描述

4.友元成员函数

通过声明友元成员函数,这样,就只有这个函数能访问另一个类的private成员。 使类B中的成员函数成为类A的友元函数,这样类B的该成员函数就可以访问类A的所有成员了。

我们来看一个例子。


#include<iostream>
using namespace std;
 
class Date;//对Date类的提前引用声明
class Time
{
public:
    Time(int h, int m, int s)
    {
    hour = h;
    minute = m;
    sec = s;
    }
    void display(Date &);
private:
    int hour;
    int sec;
    int minute;
};
 
class Date
{
public:
   Date(int m, int d, int y)
{
    mouth = m;
    day = d;
    year = y;
}
    friend void Time::display(Date &);
private:
    int mouth;
    int day;
    int year;
}void Time::display(Date &d)
{
    cout << d.mouth << "/" << d.day << "/" << d.year << endl;
    cout << hour << ":" << minute << ":" << sec << endl;
}
 
int main(void)
{
    Time t1(10, 13, 56);
    Date d1(4, 15, 2019);
    t1.display(d1);
    return 0;
}

在这里插入图片描述1.在函数名display的前面要加上display所在的对象名如(t1)
2.display成员函数的实参是Date类对象d1,f否则就不能访问对象d1的私有数据
3.在Time::display函数中引用Date类私有数据时必须加上对象名,如d.mouth.

5.破坏者?

我们已经介绍了关于友元的知识,我们知道他可以打破类的限制,但是这种打破,却不应该是随意的。如果我们使用友元,就会将类的封装性破坏,不恰当地使用友元,对程序而言十分危险。所以使用友元一定要慎重。就像我们再生活中,交友一定要谨慎。高山流水,伯牙子期是难得的,万一你和张三是好朋友,他会拉着你一块去做狂徒的,谨慎啊。
在这里插入图片描述

后记

好的,今天我们关于友元的分享就到这里了。希望对大家有所帮助。关于友元,我们知道他可以打破封装,但是这在打破的同时也是一种破坏。
最近时常能刷到一句话,“就算是friend中也藏着一个end”,我觉得这个end不应该理解为友谊会走到终点,而应该理解为友谊的边界,即使是最好的朋友,也有一个能抵达的end,这是彼此之间的边界。保留着彼此的独立人格,同时又可以共同分享,共同承担。在我们的友元里也是如此,打破封装,但要谨慎打破,不随意打破。
最后,我们分享一下胡适先生写下的中国第一首白话诗《两只蝴蝶》(原题朋友)

   两只黄蝴蝶,双双飞上天;

  不知为什么,一个忽飞还。

  剩下那一只,孤单怪可怜;

  也无心上天,天上太孤单。
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-13 11:45:37  更:2021-08-13 11:46:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 6:05:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码