C++友元(友元函数,友元类,友元成员函数)

本文详细解释了C++中友元的概念,包括友元函数、友元类和友元成员函数的声明和使用,以及它们如何让外部函数或类访问私有类成员。通过实例演示了友元在实际编程中的应用。
摘要由CSDN通过智能技术生成

1.什么是友元

生活中你的家有客厅(public),有你的卧室(private)

客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去

但是呢,你也可以让A进去,不过实现这个的关键是让A称为你的好朋友。

让A成为你的好朋友的这个过程就是友元

我们都知道,在类外我们是无法直接访问类对象的私有数据的,只能通过类的公有方法来访问,有的时候这又显得有点严格,在这种情况下,C++提供了另外一种形式的访问权限:友元

类里有公有部分(public)和私有部分(private)

类外部可以直接访问公有部分,但是私有部分是私有的,只能在类里被直接访问

但是呢你可以让普通全局函数直接访问私有部分,关键就是让该全局函数成为该类的朋友,即友元函数。

2.友元的作用

友元的目的 就是让一个函数或者类 访问另一个类中的私有成员

为了实现友元,C++引入friend这个关键字

友元有3种:友元函数,友元类,友元成员函数。

3.全局函数做友元

我们可以让普通的全局函数成为友元函数

第一步就是在将其声明(注意是声明)放在类声明里,并在原型声明前加关键字friend,这也叫友元声明,格式如下

firend 返回类型 函数名 (参数列表);

这句话的作用就像告诉编译器这个函数是我这个类的好朋友,可以访问我这个类的私有数据

友元声明可以位于public,private,protected部分,其所在的位置无关紧要

我们要注意啊,友元是朋友哦,所以不是自家人,不用限定的名称来定义

第二步就是在类外定义该全局函数,格式如下

返回类型 函数名 (参数列表)     //注意不用写friend
{
函数体
}

这样子就大功告成了

我们看个例子

#include<iostream>
using namespace std;
class AA
{//友元声明
	friend void A(AA a);//告诉编译器A函数是AA类的好朋友,可以访问AA的私有成员
private:
	int a_;
public:
	AA(int a)
	{
		a_ = a;
	}
	~AA(){}
};

void A(AA a)//友元函数
{
	cout << a.a_<< endl;
}

int main()
{
	AA a = { 3 };
	A(a);
}

结果是3.

4.类作友元

类作友元的步骤和友元函数差不多

第一步先准备好我们的好朋友(友元类),即先声明友元类

第一步就是在将友元类声明(注意是声明)放在类声明里,并在原型声明前加关键字friend,这也叫友元声明,格式如下

firend class 友元类名;

这句话的作用就像告诉编译器声明的这个类是我这个类的好朋友,可以访问我这个类的私有数据

友元声明可以位于public,private,protected部分,其所在的位置无关紧要

这样子就大功告成了

看个例子

#include<iostream>
using namespace std;
class BB
{//友元声明
	friend class AA;//告诉编译器AA类是BB类的好朋友,可以访问BB类的私有数据
private:
	int a;
public:
	BB(int a_)
	{
		a = a_;
	}

};
class AA
{
public:
	void visit(BB t)
	{
		cout << t.a << endl;
	}
};
int main()
{
	BB e = { 2 };
	AA w;
	w.visit(e);//结果是2
}

5.成员函数作友元

第一步就是在将其声明(注意是声明)放在类声明里,并在原型声明前加关键字friend,这也叫友元声明,格式如下

firend 返回类型 类名::函数名 (参数列表);

这句话的作用就像告诉编译器这个函数是我这个类的好朋友,可以访问我这个类的私有数据

友元声明可以位于public,private,protected部分,其所在的位置无关紧要

第二步就是在类外定义该全局函数,格式如下

返回类型 类名::函数名 (参数列表)     //注意不用写friend
{
函数体
}

我的建议是等到两个类声明都写完之后再去给两个类的函数进行定义 

这样子就大功告成了

#include<iostream>
using namespace std;
class AA;//先声明类

class BB
{
public:
	void B1(AA t);
	void B2(AA t);
};

class AA
{//声明引用
	friend void BB::B1(AA t);//告诉编译器BB类里的B1函数是AA类的好朋友,可以访问AA类的私有数据
private:
	int a_;
public:
	AA(int a)
	{
		a_ = a;
	}
};
	void BB::B1(AA t)
 {
		cout << t.a_<< endl;//这是可以的
	}
	void BB::B2(AA t)
	{
		cout < t.a_ << endl;//这是不行的
	}

int main()
{
	AA r = { 2 };
	BB t;
	t.B1(r);//这是可以的
	t.B2(r);//这是不行的
}

6.友元函数和运算符重载

我们在前面学习了重载运算符,赋予C++运算符多种含义。

但是我们还没有更深入的理解重载运算符和友元函数之间的爱恨情仇

我们先看作为类成员的重载运算符的例子

#include<iostream>
using namespace std;
class AA
{
private:
	int a_;
public:
	AA(int a)
	{
		a_ = a;
	}
	int operator+(int x)
	{
		return a_ + x;
	}
};
int main()
{
	AA a = { 2 };
	int b = 8;
	cout << a + b << endl;//结果是10
	cout << b + a << endl;//这是不行的
}

我们发现把左操作数和右操作数换个位置就发现不行了

这是为什么呢?

因为调用重载运算符的规则是左操作数是调用对象

也就是说上面这个例子里,a+b会被展开为a.operator+(b);

而b+a无法被展开

那我们怎么做呢?

有人说,我们再像下面再定义一个的不就好了吗

class AA
{
private:
	int a_;
public:
	AA(int a)
	{
		a_ = a;
	}
	int operator+(int x)
	{
		return a_ + x;
	}
	int operator+(int x, AA t)//编译器不允许的
	{
		return x+t.a_
	}
};

但是问题来了,这是编译器不允许的

那我们到底怎么解决这个问题呢?答案是用友元函数

要为类重载运算符,并将非类的项作为其第一个操作数,则可以用友元函数来反转操作数的顺序

#include<iostream>
using namespace std;
class AA
{
private:
	int a_;
public:
	AA(int a)
	{
		a_ = a;
	}
	int operator+(int x)
	{
		return a_ + x;
	}
	friend int operator+(int x, AA t);//告诉编译器,这个函数是AA类的朋友,可以访问AA类的私有数据
};
int operator+(int x, AA t)
{
	return x + t.a_;
}

int main()
{
	AA a = { 2 };
	int b = 8; 
	AA c = { 10 };
	cout << a + b << endl;
	cout << b + a << endl;
	cout << b + a + c << endl;
	cout << a + b + c<< endl;
}

这下子我们就解决这个问题啦,我们甚至发现居然可以连加

这是因为C++从左至右读取语句

a+b+c被展开为a.operator+(b+c)

再被展开为a.operator+(operator+(b,c));

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值