3.8 友元:友元函数和友元类
友元函数 :既可以是不属于任何类的非成员函数,也可以是另一个类的成员函数,统称为友元函数。友元函数不是当前类的成员函数,而是独立于类的外部函数,但它可以访问该类所有的成员,包括私有成员、保护成员和公有成员。在类中声明友元函数时,需在其函数名前加上关键字friend,此声明可以放在公有部分、也可以放在保护和私有部分。友元函数可以定义在类部,也可以定义在类的外部。
3.8.1 将非成员函数声明为友元函数
//1、将非成员函数声明为友元函数 // 例3.33 友元函数的使用 #include<iostream> using namespace std; class Gril{ public: Gril(char* n,int a) { name = new char[strlen(n)+1]; strcpy(name,n); age = a; } ~Gril() { delete []name; } friend void display(Gril &);//声明友元函数 //friend void display(Gril ); private: char* name; int age; }; void display(Gril &x) //形参是对象的引用 //void display(Gril x) //形参是对象 { cout<<"女孩的姓名是:"<<x.name<<","<<"年龄:"<<x.age<<endl; } int main() { Gril g("小丽",20); display(g); //调用友元函数,实参是对象的引用 return 0; } /* 说明:1、友元函数虽然可以访问类对象的私有成员,但它毕竟不是成员函数,因此,在类的 外部定义友元函数时,不必像成员函数那样,在函数名前加 "类名::" 2、因为友元函数不是类的成员,所以它不能直接访问对象的数据成员,也不能通过this 指针访问对象的数据成员,它必须通过作为入口参数传递进来的对象名(或对象指针、对象引用) 来访问引用对象的数据成员。 3、由于函数display是Gril类的友元函数,所以display函数可以访问Gril中私有数据成员 name、age。但是,在它们之前必须加上 "对象名." */
例1:非成员友元函数
/* 需求:例如有两个类Gril和Boy,现要求打印所有的男生和女生的名字和年龄,我们只需一个 独立的函数print就能完成,但它必须同时定义为这两个类的友元函数。 */ //例如3.34 一个函数定义同时定义为两个类的友元函数 #include<iostream> using namespace std; class Boy; //对Boy类的提前引用声明 class Gril{ public: Gril(char N[],int A) { strcpy(name,N); age = A; } friend void print(Gril &x) //声明print函数是Gril类的友元函数 { cout<<"女孩的姓名是:"<<x.name<<" "<<"年龄:"<<x.age<<endl; } private: char name[20]; int age; }; class Boy{ //声明Boy类 public: Boy(char N[],int A) { strcpy(name,N); age = A; } friend void print(Boy &y) //声明print函数是Boy类的友元函数 { cout<<"男孩的姓名是:"<<y.name<<" "<<"年龄:"<<y.age<<endl; } private: char name[20]; int age; }; int main() { Gril g1("王萌",12); //定义Gril类对象g1 Gril g2("李芳",14); //定义Gril类对象g2 Gril g3("张丽",18); //定义Gril类对象g3 Boy b1("张三",11); //定义Boy类对象b1 Boy b2("李四",19); //定义Boy类对象b2 Boy b3("王武",13); //定义Boy类对象b3 print(g1); //调用友元函数,实参是Gril对象g1 print(g2); //调用友元函数,实参是Gril对象g2 print(g3); //调用友元函数,实参是Gril对象g3 print(b1); //调用友元函数,实参是Boy对象b1 print(b2); //调用友元函数,实参是Boy对象b2 print(b3); //调用友元函数,实参是Boy对象b3 return 0; }
例2:非成员友元函数
#include<iostream> using namespace std; class Boy; //对Boy类的提前引用声明 class Gril{ public: Gril(char N[],int A) { strcpy(name,N); age = A; } friend void print(Gril &,Boy &); //声明print函数是Gril类的友元函数 private: char name[20]; int age; }; class Boy{ //声明Boy类 public: Boy(char N[],int A) { strcpy(name,N); age = A; } friend void print(Gril &,Boy &); //声明print函数是Boy类的友元函数 private: char name[20]; int age; }; void print(Gril &x,Boy &y) //定义print有元函数 { cout<<"女孩的姓名是:"<<x.name<<" "<<"年龄:"<<x.age<<endl; cout<<"男孩的姓名是:"<<y.name<<" "<<"年龄:"<<y.age<<endl; } int main() { Gril g1("王萌",12); //定义Gril类对象g1 Gril g2("李芳",14); //定义Gril类对象g2 Gril g3("张丽",18); //定义Gril类对象g3 Boy b1("张三",11); //定义Boy类对象b1 Boy b2("李四",19); //定义Boy类对象b2 Boy b3("王武",13); //定义Boy类对象b3 print(g1,b1); //调用友元函数,实参是Gril对象g1,Boy对象b1 print(g2,b2); //调用友元函数,实参是Gril对象g2,Boy对象b2 print(g3,b3); //调用友元函数,实参是Gril对象g3,Boy对象b3 return 0; }
3.8.2将成员函数声明为友元函数
除了一般的非成员函数可以作为某个类的友元外,一个类的成员函数也可以作为另一个类的友元,它是友元函数中的一种,成为友元成员函数。友元成员函数不仅可以访问自己所在类对象中的私有成员和公有成员,还可以访问friend声明语句所在类对象中的所有成员。
例3.35 一个类的成员函数作为另一个类的友元函数
#include<iostream> #include<string> using namespace std; class Gril; //对类Gril提前引用声明 class Boy{ public: Boy(char* n,int a) { name = new char[strlen(n)+1]; strcpy(name,n); age = a; } void disp(Gril ); //声明函数dis为类Boy为成员函数 ~Boy() { delete []name; } private: char* name; int age; }; class Gril{ public: Gril(char* n,int a) { name = new char[strlen(n)+1]; strcpy(name,n); age = a; } friend void Boy::disp(Gril ); //声明类Boy成员函数dis为类Gril的友元函数 ~Gril() { delete []name; } private: char* name; int age; }; void Boy::disp(Gril x) //定义类Boy的成员函数disp,同时也为类Gril的友元函数, { //形参为Gril类对象 cout<<"男孩的姓名:"<<name<<endl; //函数disp作为Boy类的成员函数 ,可以访问Boy类对象的私有成员 cout<<"男孩的年龄:"<<age<<endl; //注释同上 cout<<"女孩的姓名:"<<x.name<<endl;//函数disp作为Gril类的友元函数,可以访问Gril类对象的私有成员 cout<<"女孩的年龄:"<<x.age<<endl; //注释同上 } int main() { Boy b("陈大林",11); Gril g("张晓好",12); b.disp(g);//调用Boy类的对象b的成员函数和Gril类的友元函数disp,实参是Gril类的对象g //因为函数disp是Boy类的成员函数,所以无需通过传递对象,可以直接访问自己的私有数据成员 return 0; }
3.8.3 友元类
不仅函数可以作为一个类的友元,一个类也可以作为另一个类的友元,称为友元类。友元类
的说明方法是在另一个类声明中加入语句。
friend class 类名;
此类名是友元类的类名。这条语句可以放在公有部分,也可以放在私有部分。例如,
class Y{
...
};
class X{
...
friend class Y; //声明类Y是类X的友元类
...
};
当类Y被说明类X的友元时,类Y的所有成员函数都成为类X的友元函数,这就意味着作为
友元类Y中的所有成员函数都可以访问类X中的所有成员(包括私有成员)。
下面的例子中,声明了两个类Boy和Gril,类Boy声明为类Gril的友元,因此类Boy的成员
函数都能成为类Gril的友元函数,它们都可以访问类Gril的私有成员。
例 3.36 友元类的应用
#include<iostream> #include<string> using namespace std; class Gril; //对友元类的提前引用声明 class Boy{ public: Boy(char* n,int a) { name=new char[strlen(n)+1]; strcpy(name,n); age = a; } ~Boy() { delete []name; } void display1(Gril &); //声明函数display1为类Boy的成员函数 void display2(Gril &); //声明函数display2为类Boy的成员函数 private: char* name; int age; }; class Gril{ public: Gril(char* n,int a) { name=new char[strlen(n)+1]; strcpy(name,n); age = a; } ~Gril() { delete []name; } friend class Boy; //声明Boy为类Gril的友元类,则类Boy中的所有成员函数为Gril类的友元成员函数 private: char* name; int age; }; void Boy::display1(Gril &x) { cout<<"男孩的姓名是:"<<name<<endl; cout<<"女孩的姓名是:"<<x.name<<endl; } void Boy::display2(Gril &x) { cout<<"男孩的年龄是:"<<age<<endl; cout<<"女孩的年龄是:"<<x.age<<endl; } int main() { Boy b("陈大林",11); Gril g("张晓好",12); b.display1(g); b.display2(g); return 0; }
注意:声明一个类A为另一个类B的友元类(则类A的所有成员函数都是类B的友元函数,友元类A的所有成员函数既可以访问自己本类的所有成员,也可以访问类B的所有成员)
#include<iostream> #include<string> using namespace std; class Gril; //对友元类的提前引用声明 class Boy{ public: Boy(char* n,int a) { name=new char[strlen(n)+1]; strcpy(name,n); age = a; } ~Boy() { delete []name; } void display(Gril &); //声明函数display为类Boy的成员函数 private: char* name; int age; }; class Gril{ public: Gril(char* n,int a) { name=new char[strlen(n)+1]; strcpy(name,n); age = a; } ~Gril() { delete []name; } friend class Boy; //声明Boy为类Gril的友元类,则类Boy中的所有成员函数为Gril类的友元成员函数 private: char* name; int age; }; void Boy::display(Gril &x) { cout<<"男孩的姓名是:"<<name<<endl; cout<<"男孩的年龄是:"<<age<<endl; cout<<"女孩的姓名是:"<<x.name<<endl; cout<<"女孩的年龄是:"<<x.age<<endl; } int main() { Boy b("陈大林",11); Gril g("张晓好",12); b.display(g); return 0; } /* 说明:友元关系是单向的,不具有交换性。若声明了类X是类Y的友元类(即在类Y定义中声明X为friend类), 不等于类Y一定是X的友元,这就要看在类X中是否有相应的声明。 友元关系也不具有传递性,若类X是类Y的友元,类Y是类Z的友元,不一定类X是类Z的友元。如果想让类X 是类Z的友元类,应在类Z中作出声明。 */