27、友元的尴尬能力

1、友元的概念

  • 什么是友元?
    — 友元是C++ 中的一种关系
    — 友元关系是单向的,不能传递
    — 友元关系发生在 函数与类之间 或者 类与类 之间
    在这里插入图片描述
    左边的函数是右边类的友元,左边的函数就成了友元

2、友元的用法

  • 友元可以直接访问具体类的所有成员
  • 在类中以 friend 关键字声明友元
  • 类的友元可以是其它类或者具体函数
  • 友元不是类的一部分
  • 友元不受类中访问级别的限制

3、友元的语法

  • 在类中用 friend 关键字对函数或类进行声明
class Test
{
	double x;
	double y;

	friend void func(Test& t);
};
void func(Test& t)
{

}

在这个程序中,函数 func 是Test 类的友元,因此,函数就可以直接访问类中的所有成员

#include <stdio.h>
#include <math.h>
class Test
{
	double x;
	double y;
public:
	Test(double x, double y)
	{
		this->x = x;
		this->y = y;
	}
	double getX()
	{
		return x;

	}
	double getY()
	{
		return y;
	}
	friend double func(Test& t3, Test& t4);
};
double func(Test& t3,Test& t4)
{
	double ret = 0;
	//ret = (t4.getY() - t3.getY())*(t4.getY() - t3.getY()) +
	//	  (t4.getX() - t3.getX())*(t4.getX() - t3.getX());

	ret = (t4.y - t3.y)*(t4.y - t3.y) + 
		  (t4.x - t3.x)*(t4.x - t3.x);

	ret = sqrt(ret);
	return ret;
}
int main()
{
	Test t1(1, 2);
	Test t2(10, 20);

	printf("t1(%lf,%lf)\n", t1.getX(), t1.getY());
	printf("t2(%lf,%lf)\n", t2.getX(), t2.getY());

	printf("|(t1,t2)| = %lf\n",func(t1, t2));
	return 0;
}

在这里插入图片描述
分析:我们定义一个 func 函数来计算两个对象坐标的距离。如果我们不把 func 函数定义为Test 类的友元,那么计算函数里面我们只能借助对象参数来调用 getX()和getY()函数。如果是这样,那么一共要调用8次函数(详见程序的27 和28 行代码),极其麻烦。
如果我们把 func 函数定义为Test 类的友元,那么参数对象就可以直接访问类里面的所有成员,包括私有成员。(详见程序的 30 和 31 行代码),这样就避免了一直要访问函数

4、友元的尴尬

  • 友元是为了兼顾C语言的高效而诞生的
  • 友元直接破坏了面向对象的封装性
  • 友元在实际产品中的高效是得不偿失的
  • 友元在现代软件工程中已经逐渐被遗弃

5、注意事项

  • 友元关系不具备传递性
  • 类的友元可以是其它类的成员函数
  • 类的友元可以是某个完整的类
    — 所有的成员函数都是友元
    在这里插入图片描述
#include <stdio.h>
class classC
{
private:
	const char* n3;
public:
	classC(const char* n3)
	{
		this->n3 = n3;
	}
	friend class classB;
};
class classB
{
private:
	const char* n2;
public:
	classB(const char* n2)
	{
		this->n2 = n2;
	}
	void getClassCname(classC& c)
	{
		printf("%s\n", c.n3);
	}
	friend class classA;
};
class classA
{
private:
	const char* n1;
public:
	classA(const char* n1)
	{
		this->n1 = n1;
	}
	//A是B的友元,所以在B的类中声明,A可以任意访问B中的所有成员

	void getClassBname(classB& b)
	{
		printf("%s\n", b.n2);
	}
};


int main()
{
	classA A("A");
	classB B("B");
	classC C("C");
	A.getClassBname(B);
	B.getClassCname(C);
	return 0;
}

在这里插入图片描述
分析:其实这个程序我还是折腾了挺久的,因为它老是报错。

首先来源于我对知识点认知的错误,一开始我以为友元可以直接访问具体类的所有成员,所以我就在主函数定义用类A定义对象来访问另一个类B(A是B的友元)里面的私有成员,发现一直报错。后来才发现:声明为友元,只是允许在A的作用域里随意访问,主函数不是A的作用域。也就是说我们可以在A类里面成员函数的参数里面定义B的对象,在这个函数里面访问类B。

其次,这个类的先后顺序还是很重要的,如果你定义类A是类B的友元,类A可以随意访问类B里面的所有成员,那么你要先定义class B,再定义class A。

最后:A是B的友元,A是成员函数或者类,B一定是类。在B的类中声明,B要比A先定义出来,A可以任意访问B中的所有成员,A类的所有成员函数都可以访问B中的所有成员。

小结:

  • 友元是为了兼顾C语言的高效而诞生的
  • 友元直接破坏了面向对象的封装性
  • 友元的关系不具备传递性
  • 类的 友元可以是其它类成员的成员函数
  • 类的友元可以是某个完整类。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值