友元函数、友元类
友元函数:
- 说明: 在类型声明中将原型放入并在原型声明前加上关键字friend,但不要在定义时加上关键字friend,它的访问权限与类的成员函数的访问权限相同<声明而已为private,public,protect>
- 作用: 把其他类中函数作为自己的友员成员函数,让特定的类成员成为另一个类的友元函数,而不必让整个类成为友元,但在使用时,必须小心排列各种声明和定义顺序(且需用到向前声明)
- 向前声明格式:
- class 类名 ;//向前声明本类
- class 所用到的友员成员函数的类的类名 {....};//友元函数类的定义
- class 类名 {…}//本类的定义
- 注意: 友元函数不要在类的内部定义,不然编译不会通过
- 例子:
友元函数(内部定义):
View Code class Point { public: Point(double xx, double yy) { x=xx; y=yy; } friend void Getxy(Point &a){ cout<<"position is: ("<<a.x<<","<<a.y<<")"<<endl;} private: double x, y; };
元函数(内外部定义):
View Code class Point { public: Point(double xx, double yy) { x=xx; y=yy; } friend void Getxy(Point &a); private: double x, y; }; void Getxy(Point &a) { cout<<"position is: ("<<a.x<<","<<a.y<<")"<<endl; }
友元成员函数:
View Code class Date; //对 Date 类的向前声明 class Time //定义 Time 类 { public: Time(int,int,int); void display(Date&); //display 是 Time 类的成员函数,形参是 Date 类对象的引用 private: int hour; int minute; int sec; }; class Date { public: Date(int,int,int); friend void Time::display(Date&); //声明 Time 类的 display函数为本类的友元函数 private: int month; int day; int year; }; Time::Time(int h,int m,int s) //定义 Time 类的构造函数 { hour = h; minute = m; sec = s; } void Time::display(Date&d) //display 函数的作用是输出年、月、日和时、分、秒 { cout<<d.month<<"/"<<d.day<<"/"<<d.year<<endl; //引用 Date 类对象中的私有数据 cout<<hour<<":"<<minute<<":"<<sec<<endl; //引用本类对象中的私有数据 } Date::Date(int m,int d,int y) //定义 Date 类的构造函数 { month = m; day = d; year = y; } /* 请注意本程序的主函数中调用友元函数访问有关类的私有数据的方法: 1、在函数名 display 的前面要加 display 所在的对象名(t1); 2、display 成员函数的实参是 Date 类对象 d1,否则就不能访问 d1 中的私有数据; 3、在 Time::display 函数中引用 Date 类私有数据时必须加上对象名,如 d.month 。 */
虚函数:
- 作用: 如果想通过指向派生类对象的基类指针,调用派生类中覆盖的成员函数,可以用虚函数的方法让派生类中覆盖的成员函数被调用
- 格式: virtual 返回类型 函数名 (形参表);
- 注意事项:
- 如果定义的类作为基类,则应将那些要在派生类中重新定义的类的方式声明为虚拟的
- 如果使用指向对象的引用或指针调用虚方法,程序将使用做为对象类型的定义方法而不是使用做为了引用或指针定义的方法
- 工作原理:
- 给每个对象添加一个隐藏成员,隐藏成员中保存了个指 向函数的地址数组的指针,这种数组称为虚函数表(vtbl)虚函数表中存储了为类对象进行声明的虚函数地址,派生类对象将包含一个指向独立地址的表指针,如果派生类提供了虚函数的新的定义,该虚函数地址被添加到vtbl中,注意,无论类中包含的虚函数是1还是10个都只需在对象中添加一个地址成员,只是表大小不同而已
- 纯虚函数:
- 格式: virtual 函数类型 函数名(形参表)=0;<无函数体>
- 作用: 有时,要在基类中不能给出虚函数实现代码,这是可将这种虚函数声明为纯虚函数
- 注意: 有纯虚函数的类都是抽象类,并且抽象类是不能产生实例的.
- 虚函数使用总结
- 如果你期望派生类从新定义一个成员函数,那么你应该在基类中把此函数设置为 virtual
- 以单一指令调用不同函数,这种性质称为 Polymorphism(多态)
- 虚拟函数是 C++ 语言的 Polymorphism 性质以动态绑定的关键
- 既然抽象类中的虚函数不打算被调用,我们就不应该定义它,应该把它设置为纯虚函数(在函数声明后加 "=0" 即可)
- 我们可以说,拥有纯虚函数者为抽象类(abstract class),以别于所谓的具体类(concrete class)
- 抽象类不能产生实例,但我们可以拥有抽象类的指针,以便于操作抽象类的各个派生类
- 虚函数派生下去的仍然为虚函数,而且可以省去 virtual 关键字
- 例子:
虚函数:
View Code class A //有虚函数的基类 { public: virtual void show(){cout<<"这是基类的 A !"<<endl;} //用虚函数声明 show 函数 }; class B:public A { public: void show(){cout<<"这是派生类 B !"<<endl;} }; class C //没有虚函数的基类 { public: void show(){cout<<"这是基类 C !"<<endl;} }; class D:public C { public: void show(){cout<<"这是派生类 D !"<<endl;} }; void func( ) { A* a = new B; C* c = new D; a->show(); //通过虚函数找到子类的成员函数 show c->show(); //没有虚函数所以只能调用到基类的成员函数 show } /************************************ 调用 func 函数后的输出结果: 这是派生类 B ! 这是基类 C ! ************************************/
纯虚函数:
View Code class A //有纯虚函数的基类 { public: virtual void show() = NULL; //用纯虚函数声明 show 函数 }; //要注意的是如果类有纯虚函数那么这个类无法产生实例,并且纯虚函数的必须在其的派生类中实现 class B:public A { public: void show(){cout<<"这是派生类 B !"<<endl;}//实现基类 A 的纯虚函数 show };
利用虚函数/纯虚函数,打破子类访问权限.例子如下:
View Code #include<iostream> #include<cstdlib> using namespace std; class A{ public: virtual void output1() = 0; virtual void output2(){cout<<"this is A"<<endl;} }; class B:public A{ private: void output1(){cout<<"this is success!"<<endl;} protected: void output2(){cout<<"this is B"<<endl;} }; void main() { A* a; B b; a = &b; a->output1(); a->output2(); //通过 a 我们访问到了 B 变量 b 的私有成员函数和保护成员函数 output1,output2 //b.output1(); //b.output2();//这里我们是无权限去访问类 B 变量 b 的私有成员函数和保护成员函数 output1,output2 system("pause"); }
成员函数属性:
函数 能否被继承 成员还是友员 默认能否生成 能否为虚函数 是否可以有返回值
构造函数 × 成员 √ × ×
析构函数 × 成员 √ √ ×
= × 成员 √ √ √
& √ 任意 √ √ √
转换函数 √ 成员 × √ ×
() √ 成员 × √ √
[ ] √ 成员 × √ √
→ √ 成员 × √ √
op = √ 任意 × √ √
new √ 静态成员 × × void *
delete √ 静态成员 × × void
其他操作符 √ 任意 × √ √
其他成员 √ 成员 × √ √
友元 × 友员 × × √
友元类:
声明:
在类中用 friend class 友元类名;
作用:
在友元类的所有成员函数都可以访问此类中的所有数据
格式:
- class 友元类名 ;//向前声明本类
- class 拥有友元类的类名{....};//拥有友元类的类的定义
- class 友元类名 {…}//友元的定义
特点:
- 友元关系不能被继承
- 友元关系是单向的,不具有交换性.若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明.
- 友元关系不具有传递性.若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明
例子:
使用友元类:
View Code class B; //友元类的向前声明 class A //拥有友元类的类的定义 { int y; public: A() { y=4; } friend B; }; class B //友元类的定义 { public: void show(A &a) { cout<<a.y<<endl; } };
多个类包含一个友元类:
View Code class C; //友元类的向前声明 class A //拥有友元类的类的定义 { int x; public: A() { x=4; } friend C; }; class B //拥有友元类的类的定义 { int y; public: B() { y=6; } friend C; }; class C //友元类的定义 { public: void show(A &a,B &b) { cout<<a.x<<" "<<b.y<<endl; } }; void func() { A a; B b; C c; c.show(a,b); }
一个类包含多个个友元类:
View Code class B; //友元类向前声明 class C; //友元类向前声明 class A //拥有友元类的定义 { int x; public: A() { x=4; } friend C; friend B; }; class B //友元类的定义 { public: void show(A &a) { cout<<"the value of a is: "<<a.x<<" (in the class B)"<<endl; } }; class C //友元类的定义 { public: void show(A &a) { cout<<"the value of a is: "<<a.x<<" (in the class C)"<<endl; } }; void func() { A a; B b; C c; b.show(a); c.show(a); }