目录
多态
静态联编
普通成员函数重载可表达为两种形式:
1. 在一个类说明中重载
2. 基类的成员函数在派生类重载。有 3 种编译区分方法:
基类指针和派生类指针与基类对象和派生类对象4种可能匹配:
实现动态联编方式的前提
虚函数和动态联编
多态 Ø 多态性( Polymorphism )是指一个名字,多种语义;或界面相同,多种实现。 Ø 重载函数是多态性的一种简单形式。 Ø 虚函数允许函数调用与函数体的联系在运行时才进行,称为动态联编。
多态性的实现和联编这一概念有关。所谓联编(Binding,绑定)就是把函数名与函数体的程序代码连接(联系)在一起的过程。
联编分成两大类:静态联编和动态联编。
静态联编优点:调用速度快,效率高,但缺乏灵活性;动态联编优点:运行效率低,但增强了程序灵活性。
C++为了兼容C语言仍然是编译型的,采用静态联编。为了实现多态性,利用虚函数机制,可部分地采用动态联编。
多态从实现的角度来讲可以划分为两类:编译时的多态和运行时的多态。
编译时的多态是通过静态联编来实现的。静态联编就是在编译阶段完成的联编。编译时多态性主要是通过函数重载和运算符重载实现的。
运行时的多态是用动态联编实现的。动态联编是运行阶段完成的联编。运行时多态性主要是通过虚函数来实现的。
静态联编
Ø
联编是指一个程序模块、代码之间互相关联的过程。
Ø
静态联编,是程序的匹配、连接在编译阶段实现,也称为早期匹配。
Ø
重载函数使用静态联编。
Ø
动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编。
Ø
switch
语句和
if
语句是动态联编的例子。
普通成员函数重载可表达为两种形式:
1. 在一个类说明中重载
1. 在一个类说明中重载
例如: void Show ( int , char ) ;
void Show ( char * , float ) ;
2. 基类的成员函数在派生类重载。有 3 种编译区分方法:
(1)根据参数的特征加以区分
例如: void Show ( int , char ); 与
void Show ( char * , float ); 不是同一函数,编译能够区分
(2)使用“ :: ”加以区分
例如: A :: Show ( );
有别于 B :: Show ( );
(3)根据类对象加以区分
例如: Aobj . Show ( ) 调用 A :: Show ( )
Bobj . Show ( ) 调用 B :: Show ( )
基类指针和派生类指针与基类对象和派生类对象4种可能匹配:
Ø 直接用基类指针引用基类对象;
Ø 直接用派生类指针引用派生类对象;
Ø 用基类指针引用一个派生类对象;
Ø 用派生类指针引用一个基类对象。
9.2.1 基类指针引用派生类对象
例如:
A * p ; // 指向类型 A 的对象的指针
A A_obj ; // 类型 A 的对象
B B_obj ; // 类型 B 的对象
p = & A_obj ; // p 指向类型 A 的对象
p = & B_obj ; // p 指向类型 B 的对象,它是 A 的派生类
利用 p,可以通过 B_obj 访问所有从 A 类继承的元素 ,
但不能用 p访问 B 类自定义的元素 (除非用了显式类型转换)
#include#include using namespace std ; class A_class { char name[20] ; public: void put_name( char * s ) { strcpy_s( name, s ) ; void show_name() { cout << name << "n" ; } }; class B_class : public A_class { char phone_num[ 20 ] ; public : void put_phone( char * num ) { strcpy_s ( phone_num , num ) ; } void show_phone() { cout << phone_num << "n" ; } }; int main() { A_class * A_p ; A_class A_obj ; B_class B_obj ; A_p = & A_obj ; A_p -> put_name( "Wang xiao hua" ) ; A_p -> show_name() ; A_p = & B_obj ; A_p -> put_name( "Chen ming" ) ; A_p -> show_name() ; B_obj.put_phone ( "5555_12345678" ); ((B_class *) A_p ) -> show_phone() ; }
派生类指针只有经过强制类型转换之后,才能引用基类对象
#includeusing namespace std ; class Date{ public: Date( int y, int m, int d ) { SetDate( y, m, d ); } void SetDate( int y, int m, int d ) { year = y ; month = m ; day = d ; } void Print() { cout << year << '/' << month << '/' << day << "; " ; } protected : int year,month,day; } ; class DateTime : public Date { public : DateTime( int y, int m, int d, int h, int mi, int s ):Date( y, m, d ) { SetTime( h, mi, s ); } void SetTime( int h, int mi, int s ) { hours = h; minutes = mi; seconds = s; } void Print() { ( ( Date * ) this ) -> Print(); cout << hours << ':' << minutes << ':' << seconds << 'n' ; } private: int hours , minutes , seconds ; }; int main() { DateTime dt( 2009, 1, 1, 12, 30, 0 ) ; dt.Print() ; }
实现动态联编方式的前提
●先要声明虚函数
●类之间满足赋值兼容规则
●通过指针与引用来调用虚函数。
虚函数和动态联编 Ø 冠以关键字 virtual 的成员函数称为虚函数 Ø 实现运行时多态的关键首先是要说明虚函数,另外,必须用
基类指针调用派生类的不同实现版本
基类指针虽然获取派生类对象地址,却只能访问派生类从基类继承的成员
#includeusing namespace std ; class Base { public : Base(char xx) { x = xx; } void who() { cout << "Base class: " << x << "n" ; } protected: char x; } ; class First_d : public Base { public : First_d(char xx, char yy):Base(xx) { y = yy; } void who() { cout << "First derived class: "<< x << ", " << y << "n" ; } protected: char y; } ; class Second_d : public First_d { public : Second_d( char xx, char yy, char zz ) : First_d( xx, yy ) { z = zz; } void who() { cout << "Second derived class: "<< x << ", " << y << ", " << z << "n" ; } protected: char z; } ; int main() { Base B_obj( 'A' ) ; First_d F_obj( 'T', 'O' ) ; Second_d S_obj( 'E', 'N', 'D' ) ; Base * p; p = & B_obj; p -> who(); p = &F_obj; p -> who(); p = &S_obj; p -> who(); F_obj.who(); ((Second_d *)p)->who() ; }
通过基类指针只能访问从基类继承的成员!!!
#includeusing namespace std ; class Base { public : Base(char xx) { x = xx; } virtual void who() { cout << "Base class: " << x << "n" ; } //基类定义虚函数 protected: char x; } ; class First_d : public Base { public : First_d(char xx, char yy):Base(xx) { y = yy; } void who() { cout << "First derived class: "<< x << ", " << y << "n" ; } protected: char y; } ; class Second_d : public First_d { public : Second_d( char xx, char yy, char zz ) : First_d( xx, yy ) { z = zz; } void who() { cout<<"Second derived class: "< who() ; p = &F_obj ; p -> who() ; p = &S_obj ; p -> who() ; }
如此一来,基类指针便可以根据不同的类,实现相同函数对应不同类的不同的功能!!!
注意:
- 一个虚函数,在派生类层界面相同的重载函数都保持虚特性
- 虚函数必须是类的成员函数
- 不能将友元说明为虚函数,但虚函数可以是另一个类的友元
- 析构函数可以是虚函数,但构造函数不能是虚函数
- 在派生类中重载基类的虚函数要求函数名、返回类型、参数个数、参数类型和顺序完全相同
- 如果仅仅返回类型不同,C++认为是错误重载
- 如果函数原型不同,仅函数名相同,丢失虚特性



