友元(C++学习笔记 28)

  在不放弃私有成员(或保护成员)数据安全性的情况下,使得一个普通函数或者类的成员函数可以访问到封装在某一类中的信息(私有、保护成员)。
  C++中的友元为数据隐藏这堵不透明的墙开了一个小孔,外界可以通过这个小孔窥视类内部的秘密,友元是一扇通向私有(保护)成员的后门。如果一个类声明了许多友元,则相当于在墙上开了好多小孔,显然这将破坏数据的隐蔽性和类的封装性,降低了程序的可维护性,因此使用友元函数应谨慎。

C++引入友元机制的目的:
①友元机制是对类的封装机制的补充,利用友元机制,一个类可以赋予某些函数访问它的私有成员的特权。如果没有友元机制,外部函数访问类的私有数据,必须通过调用公有的成员函数,这在需要频繁调用私有数据的情况下,会带来较大的开销,从而降低程序的运行效率。
②友元提供了不同类的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。尤其当一个函数需要访问多个类时,友元函数非常有用,普通的成员函数只能访问其所属的类,但是多个类的友元函数能够访问相关的所有类的数据。

友元包括 友元函数友元类

一、友元函数

  • 既可以是不属于任何类的非成员函数,也可以是另一个类的成员函数,统称为友元函数。
  • 友元函数不是当前类的成员函数,而是独立于当前类的外部函数,但它可以访问该类所有的成员,包括私有成员、保护成员和公有成员。
  • 在类中声明友元函数时,需在其函数名前加上关键字 friend。此声明可以放在公有部分,也可以放在保护部分和私有部分。
  • 友元函数可以定义在类内部,也可以定义在类外部。

1、将非成员函数声明为友元函数。

例 1:友元函数的使用。

#include<iostream>
#include<string.h>
using namespace std;
class Girl{
	public:
		Girl(char *n,int d){	//定义构造函数,为 *name 和 age赋初值
			name=new char[strlen(n)+1];
			strcpy(name,n);
			age=d; 
		}
		friend void disp(Girl &);	//声明disp函数为类Girl的友元函数 
		void show();
		~Girl(){
			delete name;
		}
	private:
		char *name;
		int age;
};
void disp(Girl &x){
	cout<<"女孩的姓名是:"<<x.name<<";年龄:"<<x.age<<endl;
}
void Girl::show(){
	cout<<"the girl's name is:"<<name<<";age is:"<<age<<endl;
}
int main(){
	Girl g1("小红",18);
	disp(g1);
	g1.show();
	return 0;	
}

说明:
(1)友元函数虽然可以访问类对象的私有成员,但它毕竟不是成员函数,因此在类的外部定义友元函数时,不必像成员函数那样,在函数名前加上“类名::”。
(2)因为友元函数不是类的成员,所以它不能直接访问对象的数据成员,也不能通过this指针访问对象的数据成员,它必须通过作为入口参数传递进来的对象名(或对象指针,对象引用)来访问引用该对象的数据成员。
(3)由于函数disp是Girl类的友元函数,所以disp函数可以访问Girl中的私有数据成员name和age。但在访问name 和 age时,必须加上对象名 x。

例 2:一个函数同时定义为两个类的友元函数。

例如有 Boy 和 Girl 两个类,现要求打印出所有男生和女生的名字和年龄。只需一个独立的函数 prdata 就能够完成,但是它必须同时定义为这两个类的友元函数。

#include<iostream>
#include<string.h>
using namespace std;
class Boy;  //对Boy类的提前引用声明,第8行使用到了Boy类 
class Girl{
	public:
		Girl(char N[],int A);	
		friend void prdata(const Girl &,const Boy &);  //声明 prdata 函数为类 Girl 的友元函数 
	private:
		char name[25];
		int age;
};
Girl::Girl(char N[],int A){
	strcpy(name,N);
	age=A;
}
class Boy{
	public:
		Boy(char N[],int A);
		friend void prdata(const Girl &plg,const Boy &plb);  //声明 prdata 函数为类 Boy 的友元函数 
	private:
		char name[25];
		int age;
};
Boy::Boy(char N[],int A){
	strcpy(name,N);
	age=A;
}
void  prdata(const Girl &plg,const Boy &plb){ //定义函数 prdata 为类 Girl 和 Boy 的友元函数,
											  //形参 plg 和 plb 分别是 Girl 类和 Boy 类的对象的引用 
	cout<<"女孩的姓名:"<<plg.name<<"\n";
	cout<<"女孩的年龄:"<<plg.age<<"\n";
	cout<<"男孩的姓名:"<<plb.name<<"\n";
	cout<<"男孩的年龄:"<<plb.age<<"\n";
}
int main(){
	Girl g1("小红",18);
	Girl g2("小芳",17);
	Girl g3("小夏",18);
	Boy b1("小明",18);
	Boy b2("小强",19);
	Boy b3("小刚",18);
	prdata(g1,b1);
	prdata(g2,b2);
	prdata(g3,b3);

	return 0;	
}

执行结果:
在这里插入图片描述
由于函数 prdata 被定义成类 Boy 和 Girl 的友元函数,所以它能够访问者两个类中的所有数据(包括私有数据)。

2、将成员函数声明为友元函数

  一个类的成员函数也可以作为另一个类的友元,它是友元的一种,称为友元成员函数。
  友元成员函数不仅可以访问自己所在类对象中的私有成员和公有成员,还可以访问 friend 声明语句所在类对象中的所有成员,这样能使两个类相互合作,协调工作,完成某一任务。

例 3:声明 disp 为类 Girl 的成员函数,又是类 Boy 的友元函数。

#include<iostream>
#include<string.h>
using namespace std;
class Boy;  //对Boy类的提前引用声明,第8行使用到了Boy类 
class Girl{
	public:
		Girl(char *n,int d){
			name=new char[strlen(n)+1];
			strcpy(name,n);
			age=d;			
		}	
		void prdata(Boy &);  
		~Girl(){
			delete name;
		} 
	private:
		char *name;
		int age;
};
class Boy{
	public:
		Boy(char *n,int d){
			name=new char[strlen(n)+1];
			strcpy(name,n); 
			age=d;
		}
		friend void Girl::prdata(Boy &plb);  //声明类 Girl 的成员函数 prdata 函数为类 Boy 的友元函数 
		~Boy(){
			delete name;
		}
	private:
		char *name;
		int age;
};

void  Girl::prdata(Boy &x){ 
	cout<<"女孩的姓名:"<<name<<"\n";
	cout<<"女孩的年龄:"<<age<<"\n";
	cout<<"男孩的姓名:"<<x.name<<"\n";
	cout<<"男孩的年龄:"<<x.age<<"\n";
}
int main(){
	Girl g1("小红",18);
	Boy b1("小明",18);
	g1.prdata(b1);
	return 0;	
} 

执行结果:
在这里插入图片描述
说明:
  一个类的成员函数作为另一个类的友元函数时,必须先定义这个类。比如,在例3中类 Girl 的成员函数为类 Boy 的友元函数,必须先定义类 Girl,并且在声明友元函数时要加上成员函数所在类的类名。

二、友元类

  • 不仅函数可以作为一个类的友元,一个类也可以作为另一个类的友元,称为友元类。
  • 友元类的声明是在另一个类声明中加入语句:friend 类名;
    此类名为友元类的类名。这条语句可以放在公有部分也可以放在私有部分。例如:
class Y{
······
};
class X{
······
friend Y;  //声明类Y为类X的友元类
······
};

当类 Y 被说明为类 X 的友元时,类 Y 的所有成员函数都成为类 X 的友元函数,即作为友元类 Y 中的所有成员函数都可以访问类 X 中的所有成员(包括私有成员)。

例 4:友元类的应用。

#include<iostream>
#include<string.h>
using namespace std;
class Boy;  //对Boy类的提前引用声明,第8行使用到了Boy类 
class Girl{
	public:
		Girl(char *n,int d){
			name=new char[strlen(n)+1];
			strcpy(name,n);
			age=d;			
		}	
		void prdata(Boy &);  
		~Girl(){
			delete name;
		} 
	private:
		char *name;
		int age;
};
class Boy{
	public:
		Boy(char *n,int d){
			name=new char[strlen(n)+1];
			strcpy(name,n); 
			age=d;
		}
		friend Girl;  //例4和例3的唯一一处不同
		~Boy(){
			delete name;
		}
	private:
		char *name;
		int age;
};

void  Girl::prdata(Boy &x){ 
	cout<<"女孩的姓名:"<<name<<"\n";
	cout<<"女孩的年龄:"<<age<<"\n";
	cout<<"男孩的姓名:"<<x.name<<"\n";
	cout<<"男孩的年龄:"<<x.age<<"\n";
}
int main(){
	Girl g1("小红",18);
	Boy b1("小明",18);
	g1.prdata(b1);
	return 0;	
} 

程序的执行结果同例3。
  在例4中,声明了类 Girl 为类 Boy 的友元类,则类 Girl 中的所有成员函数为类 Boy 的友元成员函数,因此类 Girl 中的成员函数 prdata 是类 Boy 的友元成员函数,它不但可以访问类 Girl 中的所有成员,也可以访问类 Boy 中的所有成员。

说明:
  友元关系是单向的,不具有交换性。若声明了类 X 是类 Y 的友元(即在类 Y 定义中声明类 X 为friend类),不等于类 Y 一定是 X 的友元,这要看在类 X 中是否有相应的声明。
  友元关系也不具有传递性,若类 X 是类 Y 的友元,类 Y 是类 Z 的友元,不一定类 X 是类 Z 的友元。如果想让类 X 是 类 Z 的友元类,应在类 Z 中作声明。

猜你喜欢

转载自blog.csdn.net/aaqian1/article/details/84427884
c28