友元C++BULIDER讲稿_友元的说明和定义
友元C++BULIDER讲稿由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“友元的说明和定义”。
友元
类的主要特点之一是数据隐藏,即类的私有成员只能在类定义的范围内使用,也就是
说私有成员只能通过它的成员函数来访问。但是,有时候需要在类的外部访问类的私有成员。为此,就需要寻找一种途径,在不放弃私有数据安全性的情况下,使得类外部的函数或
类能够访问类中的私有成员,在C++中就用友元作为实现这个要求的辅助手段。C++中的友元为数据隐藏这堵不透明的墙开了一个小孔,外界可以通过这个小孔窥视类内部的秘密,友元是一扇通向私有成员的后门。
友元既可以是不属于任何类的一般函数,也可以是另一个类的成员函数,还可以是整
个的一个类(这样,这个类中的所有成员函数都可以成为友元函数)。
3.6.1 友元函数
友元函数不是当前类的成员函数,而是独立于当前类的外部函数,但它可以访问该类的所有对象的成员,包括私有成员和公有成员。
在类定义中声明友元函数时,需在其函数名前加上关键字friend。此声明可以放在公有部分,也可以放在私有部分。友元函数可以定义在类内部,也可以定义在类的外部。
下面是一个使用友元函数的例子。例3.1 #include #include cla girl{
char *name;
int age;public:
girl(char *n,int d)
{ name=new char[strlen(n)+1];
strcpy(name,n);
age=d;
}
friend void disp(girl &);
//声明友元函数
~girl()
{ delete name;} };
void disp(girl &x)
//定义友元函数
{
cout
}
void main(){ girl e(“Chen Xingwei”, 18);
disp(e);
//调用友元函数 }
程序的运行结果如下:
girl's name is:Chen Xingwei,age:18
从上面的例子可以看出,友元函数可以访问类对象的各个私有数据。若在类girl的声 明中将友元函数的声明语句去掉,那么函数disp()对类对象的私有数据的访问将变为非法的。说明:
(1)友元函数虽然可以访问类对象的私有成员,但它毕竟不是成员函数。因此,在类的外部定义友元函数时,不必像成员函数那样,在函数名前加上“类名::”。
(2)友元函数一般带有一个该类的入口参数。因为友元函数不是类的成员,所以它不能直接引用对象成员的名字,也不能通过this指针引用对象的成员,它必须通过作为入口参数传递进来的对象名或对象指针来引用该对象的成员。
例如上面例子中的友元函数void disp(girl &x)就带有该类的一个入口参数。(3)当一个函数需要访问多个类时,友元函数非常有用,普通的成员函数只能访问其所属的类,但是多个类的友元函数能够访问相应的所有类的数据。
例如有boy和girl两个类,现要求打印出所有的男生和女生的名字和年龄,我们只需 一个独立的函数prdata()就能够完成,但它必须同时定义为这两个类的友元函数。下例
给出了这样的一个程序。例3.2
#include #include cla boy;
//向前引用 cla girl{
char name[25];
int age;public:
void init(char N[],int A);
friend void prdata(const girl plg,const boy plb);//声明prdata()为girl的友元函数 };void girl::init(char N[],int A){ strcpy(name ,N);
age=A;} cla boy{
char name[25];
int age;public:
void init(char N[],int A);
friend void prdata(const girl plg,const boy plb);//声明prdata()为boy的友元函数 };void boy::init(char N[],int A){ strcpy(name,N);
age=A;} void prdata(const girl plg,const boy plb)//定义友元函数prdata(){ cout
cout
cout
cout
boy B1,B2,B3;
G1.init(“Stacy”, 12);
G2.init(“Judith”, 13);
G3.init(“Leah” ,12);
B1.init(“Jim”,11);
B2.init(“Michael”, 13);
B3.init(“ Larry”, 12);
prdata(G1, B1);
//调用友元函数prdata()
prdata(G2, B2);
//调用友元函数prdata()
prdata(G3, B3);
//调用友元函数prdata()} 程序运行结果如下;
name:Stacy
age:12
name:Jim
age:11 name:Judith age:13 name:Michael
age:13
name:Leah
age:12
name:Larry
age:12
程序中的第3行是由于第9行的要求而存在的。因为友元函数带了两个不同的类的对象,其中一个是类boy的对象,而类boy要在晚一些时候才被声明。为了避免编译时的错误,编程时必须通过向前引用(forward reference)告诉C++类boy将在后面定义。在向前引用类声明之前,可以使用该类声明参数,这样第9行就不会出错了。
prdata()是程序中的一个独立函数,可以被main()或其它任意函数调用。但由于它被
定义成类boy和类girl的友元函数,所以它能够访问这两个类中的私有数据。
⑷友元函数通过直接访问对象的私有成员,提高了程序运行的效率。在某些情况下,如运算符被重载时,需要用到友元。但是友元函数破坏了数据的隐蔽性,降低了程序的可维护性,这与面向对象的程序设计思想是背道而驰的,因此使用友元函数应谨慎。
3.6.2 友元成员
除了一般的函数可以作为某个类的友元外,一个类的成员函数也可以作为另一个类的友元,这种成员函数不仅可以访问自己所在类对象中的私有成员和公有成员,还可以访
问friend声明语句所在类对象中的私有成员和公有成员,这样能使两个类相互合作、协调
工作,完成某一任务。
在例3.3所列的程序中,声明了disp()为类boy的成员函数,又是类girl的友元函数。例3.3 #include #include cla girl;cla boy {
char * name;
int age;public:
boy(char *n,int d)
{ name=new char[strlen(n)+1];
strcpy(name,n);
age=d;
}
void disp(girl &);
//声明disp()为boy的成员函数
~boy()
{ delete name;} };cla girl {
char *name;
int age;public:
girl(char *n,int d)
{ name=new char[strlen(n)+1];
strcpy(name,n);
age=d;
}
friend void boy::disp(girl &);
//声明boy的成员函数disp()为girl的友元函数
~girl()
{ delete name;} };
void boy::disp(girl &x)//定义友元函数disp(){ cout
cout
girl e(“zhang wei”,18);
b.disp(e);} 程序运行结果如下:
boy's name is:chen hao,age:25
girl's name is:zhang wei,age:18
说明:
(1)一个类的成员函数作为另一个类的友元函数时,必须先定义这个类。例如上例中,类boy的成员函数为类girl的友元函数,必须先定义类boy。并且在声明友元函数时,要加上成员函数所在类的类名,如:
friend void boy::disp(girl&);
(2)程序中第3行“cla girl;”为向前引用,因为函数disp()中将girl&作为参数,而girl在要晚一些时候才被定义。
3.6.3 友元类
不仅函数可以作为一个类的友元,一个类也可以作为另一个类的友元。这种友元类的说明方法是在另一个类声明中加入语句“friend 类名(即友元类的类名),”,此语句可以
放在公有部分也可以放在私有部分,例如:
cla Y{ //….};
cla X{
//….friendY;
//声明类Y为类X的友元类 //…
};
当一个类被说明为另一个类的友元时,它的所有的成员函数都成为另一个类的友元
函数,这就意味着作为友元的类中的所有成员函数都可以访问另一个类中的私有成员。
下面的例子中,声明了两个类boy和girl,类boy声明为类girl的友元,因此类boy的成员函数都成为类girl的友元函数,它们都可以访问类girl的私有成员。
例3.25 #include #include cla girl;cla boy {
char *name;
int age;public:
boy(char *n,int d)
{ name=new char[strlen(n)+1];
strcpy(name,n);
age=d;
}
void disp(girl &);
//声明disp()为类boy的成员函数
~boy()
{ delete name;} };cla girl {
char * name;
int age;
friend boy;
//声明类boy是类girl的友元 public:
girl(char *n,int d)
{ name=new char[strlen(n)+1];
strcpy(name,n);
age=d;
}
~girl()
{ delete name;} };void boy::disp(girl &x)//定义函数disp()为类boy的成员函数,//也是类girl的友元函数 { cout
cout
girl e(”zhang wei“,18);
b.disp(e);}
程序运行结果如下:
boy's name is:chen hao,age:25
girl's name is:zhang wei,age:18
说明:友元关系是单向的,不具有交换性。若类X是类Y的友元(即在类Y定义中声明X为friend类),类Y是否是X的友元,要看在类中是否有相应的声明。友元关系也不具有传递性,若类X是类Y的友元,类Y是类Z的友元,不一定类X是类Z的友元。
3.7 类对象作为成员
在类定义中定义的数据成员一般都是基本的数据类型。但是类中的成员也可以是对
象,叫做对象成员。使用对象成员着重要注意的问题是构造函数的定义方式,即类内部对象的初始化问题。
含有对象成员的类,其构造函数和不含对象成员的构造函数有所不同,例如有以下的类:
cla X{
类名1 成员名1:
类名2 成员名2;
……
类名n 成员名n;
};
一般来说,类X的构造函数的定义形式为:
X::X(参数表0):成员名1(参数表1),„,成员名n(参数表n){
//构造函数体
} 冒号后面的部分是对象成员的初始化列表,各对象成员的初始化列表用逗号分隔,参数表i(i为1到n)给出了初始化对象成员所需要的数据,它们一般来自参数表。
当调用构造函数X::X()时,首先按各对象成员在类定义中的顺序依次调用它们的构造函数,对这些对象初始化,最后再执行X::X()的函数体。析构函数的调用顺序与此相反。
下面的例子中有两个类string和girl,类girl中的一个数据成员name是类string的一个对象,即name是类girl的一个对象成员。两个类都有各自的构造函数,而类girl的构造函数被执行时,希望能给其中的string类的成员对象也初始化为具体的值。
例3.26 #include #include cla string{
//声明类string
private:
char *str;
public:
string(char *s)
//定义类string的构造函数
{ str=new char[strlen(s)+1];
strcpy(str, s);
}
void print()
{ cout
~string()
{ delete str;} };cla girl{
//声明类girl private:
string name;
//name为类girl的对象成员
int age;public:
girl(char *st, int ag):name(st)
//定义类girl的构造函数
{ age=ag;}
void print()
{ name.print();
//调用类string的对象name的成员函数print()
cout
}
~girl()
{ } };void main(){ girl obj(”Chen hao",25);
obj.print();}
程序运行结果如下: chen hao age:25 说明:
(1)声明一个含有对象成员的类,首先要创建各成员对象。本例在声明类girl中,定义了对象成员name:
string name;
(2)girl类对象在调用构造函数进行初始化的同时,也要对对象成员进行初始化,因为它也是居于此类的成员。因此在写类girl的构造函数时,也缀上了对对象成员的初始化: girl(char *st,int ag):name(st)于是在调用girl的构造函数进行初始化时,也给对象成员name赋上了初值。
这里需要注意的是:在定义类girl的构造函数时,必须缀上其对象成员的名字name,而不能缀上类名,若写成;
girl(char *st,int ag):string(st)是不允许的,因为在类girl中是类string的对象name作为成员,而不是类string作为其成员。