简述
- 多态性(Polymorphism)是指一个名字,多种语义;或界面相同,多种实现。
- 重载函数是多态性的一种简单形式。
- 虚函数允许函数与函数体的联系再运行是菜进行,称为动态联编。
多态的实现
- 多态性的实现和联编这一概念有关。所谓联编(Binding,绑定)就是把函数名与函数体的程序代码连接(联系)在一起的过程。
- 联编分成两大类:静态联编和动态联编。
- 静态联编优点:调用速度快,效率高,但缺乏灵活性;动态联编优点:运行效率低,但增强了程序灵活性。
- C++为了兼容C语言仍然是编译型的,采用静态联编。为了实现多态性,利用虚函数机制,可部分地采用动态联编。
- 多态从实现的角度来讲可以划分为两类:编译时的多态和运行时的多态。
- 编译时的多态是通过静态联编来实现的。静态联编就是在编译阶段完成的联编。编译时多态性主要是通过函数重载和运算符重载实现的。
- 运行时的多态是用动态联编实现的。动态联编是运行阶段完成的联编。运行时多态性主要是通过虚函数来实现的。
静态联编
静态联编,是程序的匹配、连接在编译阶段实现,也称为早期匹配。
重载函数使用静态联编。
普通成员函数重载课表达为两中国形式:
1.在一个类说明中重载
2.基类的成员函数在派生类重载。有三种编译区分方法:
(1).根据参数的特征加以区分
(2).使用“::”加以区分
(3).根据类对象加以区分
例如:
Aobj.Show()调用A::Show()
类指针的关系
基类指针和派生类指针与基类对象和派生类对象4种可能匹配:
- 直接用基类指针引用基类对象;
- 直接用派生类指针引用派生类对象;
- 用基类指针引用一个派生类对象;
- 用派生类指针引用一个基类对象。
例:
A * p ; // 指向类型 A 的对象的指针
A A_obj ; // 类型 A 的对象
B B_obj ; // 类型 B 的对象
p = & A_obj ; // p 指向类型 A 的对象
p = & B_obj ; // p 指向类型 B 的对象,它是 A 的派生类
派生类指针引用基类对象
#include<iostream>
#include<cstring>
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() ;
}
虚函数和动态联编
冠以关键字 virtual 的成员函数称为虚函数
实现运行时多态的关键首先是要说明虚函数,另外,必须用基类指针调用派生类的不同实现版本
基类指针虽然获取派生类对象地址,却只能访问派生类从基类继承的成员
#include<iostream>
using 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;
} ;
注意
- 一个虚函数,在派生类层界面相同的重载函数都保持虚特性
- 虚函数必须是类的成员函数
- 不能将友元说明为虚函数,但虚函数可以是另一个类的友元
- 析构函数可以是虚函数,但构造函数不能是虚函数
虚函数的析构
例如:
#include<iostream>
using namespace std ;
class A
{ public:
~A(){ cout << "A::~A() is called.\n" ; }
} ;
class B : public A
{ public:
~B(){ cout << "B::~B() is called.\n" ; }
} ;
int main()
{ A *Ap = new B ;
B *Bp2 = new B ;
cout << "delete first object:\n" ;
delete Ap;
cout << "delete second object:\n" ;
delete Bp2 ;
}
纯虚函数和抽象类
- 纯虚函数是一种特殊的虚函数;
- 在许多情况下,在及雷暴中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。
class point { /*……*/ } ;
class shape ; // 抽象类
{ point center ;
……
public :
point where ( ) { return center ; }
void move ( point p ) {center = p ; draw ( ) ; }
virtual void rotate ( int ) = 0 ; // 纯虚函数
virtual void draw ( ) = 0 ; // 纯虚函数
} ;
纯虚函数和抽象类
#include<iostream>
using namespace std ;
class Number
{ public : Number (int i) { val = i ; }
virtual void Show() = 0 ;
protected: int val ;
};
class Hex_type : public Number
{ public: Hex_type(int i) : Number(i) { }
void Show() { cout << "Hexadecimal:" << hex << val << endl ; }
};
class Dec_type : public Number
{ public: Dec_type(int i) : Number(i) { }
void Show() { cout << "Decimal: " << dec << val << endl ; }
};
class Oct_type : public Number
{ public: Oct_type(int i) : Number(i) { }
void Show() { cout << "Octal: " << oct << val << endl ; }
};
void fun( Number & n ) // 抽象类的引用参数
{ n.Show() ; }
int main()
{ Dec_type n1(50);
fun(n1); // Dec_type::Show()
Hex_type n2(50);
fun(n2); // Hex_type::Show()
Oct_type n3(50);
fun(n3); // Oct_type::Show()
}