【C++程序员的自我修炼】友元

心之所向

素履以往


目录

友元函数

cout 如何输出自定义类型

cin 如何输入自定义类型

总结

友元类

日期类Date 如何访问 时间类Time

内部类

概念:

总结:


契子✨ 

我们之前已经把类与对象的基础知识已经学完了,这些是只针对一个类的操作

那么两个类甚至多个类之间能不能有联系呢?

比如 日期类Date 想要访问 时间类Time ,配合的输出当下的具体时间

答案当然是可行的,这就要借助我们的友元来解决问题

友元的简介(百度百科):

友元是一种定义在类外部的普通函数,但它需要在类体内进行说明,为了与该类的成员函数加以区别,在说明时前面加以 关键字friend。友元不是成员函数,但是它可以访问类中的私有成员。


友元函数

先举个小栗子~

cout 如何输出自定义类型

因为我们最近写的都是自定义类型 -- class,所以想用 cout 输出自定义类型的数据就用不了了

如果我们仍想用 cout 输出数据则需要符号重载 <<

#include<iostream>
#include<cstdlib>
using namespace std;
class Date
{
public:
	Date()
		: _year(2024)
		, _month(4)
		, _day(19)
	{}
	ostream & operator<<(ostream& _cout)
	{
		cout << this->_year << " 年 " << this->_month << " 月 " << this->_day << " 日 ";
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	d1<< cout<<endl;
	system("pause");
	return 0;
}

d1<< cout 等价于 d1.operator<<(&d1, cout)

现在尝试去重载 operator<< ,然后发现没办法将 operator<< 重载成成员函数。因为 cout 的输出流对象和隐含的 this 指针在抢占第一个参数的位置。this 指针默认是第一个参数也就是左操作数了。


总觉得这种写法很别扭,有没有办法变成 cout<< d1 呢?

实际使用中 cout 需要是第一个形参对象才能正常使用,也就是 cout.operator<<(cout, &d1)

所以要将 operator<< 重载成全局函数即可

但又会导致类外没办法访问成员,此时就需要友元函数来解决


friend 加以修饰的函数便是友元函数,也就是说在这个函数中可以访问到类的成员

~就好比与我是你的好兄弟,我可以用你的东西

#include<iostream>
#include<cstdlib>
using namespace std;

class Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);
public:
	Date()
		:_year(2024)
		, _month(4)
		, _day(19)
	{}

private:
	int _year;
	int _month;
	int _day;
};

ostream& operator<<(ostream& _cout, const Date& d)
{
	cout << d._year << " 年 " << d._month << " 月 " << d._day << " 日 ";
	return _cout;
}

int main()
{
	Date d1;
	cout << d1 << endl;
	system("pause");
	return 0;
}

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

 既然输出都写了,那我们在写一个输入吧

cin 如何输入自定义类型

#include<iostream>
#include<cstdlib>
using namespace std;

class Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend istream& operator>>(istream& _cin, Date& d);
public:
	Date(int year = 2024,int month = 4,int day = 19)
		:_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;
}

int main()
{
	Date d;
	cin >> d;
	cout << d << endl;
	system("pause");
	return 0;
}

那么有老铁会问友元函数能够复用吗,比如刚刚写的自定义类型的 cout cin
当然可以 -- 一个函数可以是多个类的友元函数

总结

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
友元函数可访问类的私有和保护成员,但不是类的成员函数
友元函数不能用const修饰
友元函数可以在类定义的任何地方声明,不受类访问限定符限制
一个函数可以是多个类的友元函数
友元函数的调用与普通函数的调用原理相同

友元类

先举个小栗子~

日期类Date 如何访问 时间类Time

#include<iostream>
#include<cstdlib>
using namespace std;

class Time
{
	friend class Date;
public:
	Time()
		:_hour(6)
		,_minute(3)
		,_second(0)
	{}

private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
public:
	Date(int hour = 6, int minute = 30, int second = 6)
		:_year(2024)
		,_month(4)
		,_day(19)
	{
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}
	void Print() 
	{
		cout << this->_year << " 年 " << this->_month << " 月 " << this->_day << " 日 " << endl;
		cout << this->_t._hour << " 时 " << this->_t._minute << " 分 " << this->_t._second << " 秒 " << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};

int main()
{
	Date d1(6, 30, 48);
	d1.Print();
	system("pause");
	return 0;
}

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

以上我在 Time 类中声明了 Date 类的友元

说明 Time 已经把 Date 当成了友人,Date 想用 Time 的东西(成员)顺便用即可~

这浓浓的兄弟情便是友元的一大特点

这样老铁可能会有一个疑问?

那么 Time 可以访问 Date 的成员吗

以下代码是否正确呢?

class Time
{
	friend class Date;
public:
	Time(int year = 2024,int month =4,int day = 19)
		:_hour(6)
		,_minute(3)
		,_second(0)
	{
		_d._year = year;
		_d._month = month;
		_d._day = day;
	}

private:
	int _hour;
	int _minute;
	int _second;
	Date _d;
};

答案当然是 -- 否

你把我当兄弟,我用你的东西;但是我不把你当兄弟,我的东西你别想用

小总结:
友元关系是单向的,不具有交换性

比如上述 Time 类和 Date 类,在 Time 类中声明 Date 类为其友元类,那么可以在 Date 类中直接

访问  Time  类的私有成员变量,但想在  Time  类中访问  Date  类中私有的成员变量则不行

那么友元可以传递吗?

比如 A 和 B 是朋友,B 和 C 是朋友,那么 A 和 C 会成为朋友吗

这里举个小栗子~ 以下代码是否正确呢?

#include<iostream>
#include<cstdlib>
using namespace std;

class A
{
	friend B;
public:
	A()
		:_a(0)
	{
		_cc._c = 0;
	}
private:
	int _a;
	C _cc;
};

class B
{
	friend C;
private:
	int _b;
};

class C
{
private:
	int _c;
};

答案是 A 和 C 还不是朋友~
class C
{
	friend A;
private:
	int _c;
};
只要在 C 中声明 A 是 C 的友元才可以访问

小总结

友元关系不能传递

内部类

概念:

如果一个类定义在另一个类的内部,这个内部类就叫做内部类 。内部类是一个独立的类, 它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意: 内部类就是外部类的友元类 ,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。

简单来讲:外部类就是内部类(女神)的舔狗,女神想对舔狗做什么,舔狗都得接受;而舔狗想要的连一点温馨的问候都没有,舔狗是没有尊严的,外部类也是如此~

举个栗子~
#include<iostream>
#include<cstdlib>
using namespace std;

class A
{
public:
	class B
	{
	public:
		void  Print(const A&a)
		{
			cout << a._a << endl;
		}
	private:
		int _b;
	};
private:
	int _a = 10;
};

int main()
{
	A::B b;
	b.Print(A());
	system("pause");
	return 0;
}

其实 B (内部类)是个独立的类,只是放在 A (外部类)里面仅仅受到类域的限制

比如在声明对象的时候还要先写外部域 (A::B)

其次 B 拥有 A 的所有特权,但是 A 没有 B 的一点特权

(B天生就是A的友元,内部类可以访问外部类的私有)

外部类A 却访问不到 内部类B


那么 A 不想当舔狗怎么办呢?

我们可以把内部类设置为私有,这样就不能被外界访问了

class A
{
private:
	class B
	{
	public:
		void  Print(const A&a)
		{
			cout << a._a << endl;
		}
	private:
		int _b;
	};
private:
	int _a = 10;
};


可能有老铁会问,两个类嵌套的大小是多少呢?

#include<iostream>
#include<cstdlib>
using namespace std;

class A
{
private:
	static int k;
	int h;
public:
	class B
	{
	public:
		void foo(const A& a)
		{
			cout << k << endl;
			cout << a.h << endl;
		}
	private:
		int _b;
	};
};
int A::k = 1;
int main()
{
	cout << sizeof(A) << endl;
	system("pause");
	return 0;
}

我们来计算一下~

 我们发现 sizeof 计算的是外部类,和内部类没有任何关系

		void foo(const A& a)
		{
			cout << k << endl;
		}

注意:内部类可以直接访问外部类中的 static 成员,不需要外部类的对象

总结:

内部类可以定义在外部类的publicprotectedprivate都是可以的
注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名
sizeof计算的是外部类,和内部类没有任何关系

先介绍到这里啦~

有不对的地方请指出💞

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烟雨长虹,孤鹜齐飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值