多态分为两类
1.静态多态: 函数重载和运算符重载属于静态多态,复用函数名
2.动态多态: 派生类和虚函数实现运行时多态
静态多态和动态多态区别:
1.静态多态的函数地址早绑定 - 编译阶段确定函数地址
2.动态多态的函数地址晚绑定 - 运行阶段确定函数地址
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include<iostream> using namespace std; class Animal{ public: virtual void speak(){ cout<<"animal speak"<<endl; } }; class Cat:public Animal{ public: void speak(){ cout<<"cat speak"<<endl; } }; class Dog:public Animal{ public: void speak(){ cout<<"dog speak"<<endl; } };
void dospeak(Animal &animal){ animal.speak(); }
void test01(){ Cat cat; dospeak(cat); Dog dog; dospeak(dog); } int main(){ test01(); system("pause"); return 0; }
|
上面的代码输出为:
cat speak
dog speak
我们再来看一下本质:
Animal的内部结构在变为虚函数后是这样的:
1 2 3 4 5 6 7 8 9
| class Animal size(4) +--- 0 |{vfptr} +---
Animal::$vftable@: |&Animal_meta |0 0 |&Animal::speak
|
变为虚函数后,由vfptr这个指针指向vftable中,
Cat类重写过后内部是这样的:
1 2 3 4 5 6 7 8 9 10
| class Cat size(4) +--- 0 | +---base class Animal 0 | | {vfptr} | +--- +--- Cat::$vftable@: | &Cat_meta | 0 0 | &Cat::speak
|
由vfptr指针指向vftable,其中因为被重写过了,已经由上面的&Animal::speak变为&Cat::speak,
变为虚函数,编译器就不能确定函数调用了,又经过这样的重写,指向了Cat,这样就真正实现了多态。
======
下面再介绍纯虚函数的概念:
在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将虚函数改为纯虚函数
纯虚函数语法:virtual 返回值类型 函数名 (参数列表)= 0 ;
当类中有了纯虚函数,这个类也称为抽象类。
抽象类特点:
无法实例化对象,子类必须重写抽象类中的纯虚函数,否则也属于抽象类
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include<iostream> using namespace std; #include<string>
class Base{ public: virtual void func()=0; };
class Son:public Base{ public: virtual void func(){ cout<<"func diaoyong"<<endl; } }; void test01(){ Base *base=NULL; base = new Son; base->func(); delete base; } int main(){ test01(); system("pasue"); return 0; }
|
======
在多态的使用,如果子类中的属性有开辟到对去了,那么父类指针在释放时调用不到子类的析构函数,所以c++中将父类的析构函数改为虚析构函数或者纯虚析构函数来解决此问题。
两者的共性:
1.都可以解决父类指针释放子类对象的问题
2.但是都需要具体的函数实现,也就是说是不能自动生成两类函数的
两者的区别:
1.如果是纯虚析构函数,该类属于抽象类,无法实例化
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| #include<iostream> using namespace std; #include<string>
class Animal{ public: Animal(){ cout<<"Animal gouzao func"<<endl; } virtual void speak()=0; virtual ~Animal()=0; };
Animal::~Animal(){
cout<<"Animal pure virtual xigou func"<<endl; }
class Cat:public Animal{ public: Cat(string name){ cout<<"cat gouzao func have name"<<endl; m_name=new string(name); }
virtual void speak(){ cout<<*m_name<<"cat speak"<<endl; } ~Cat(){ cout<<"Cat xigou func"<<endl; if(this->m_name!=NULL){ delete m_name; m_name=NULL; } } public: string *m_name; };
void test01(){ Animal *animal=new Cat("tom"); animal->speak(); delete animal; }
int main(){ test01(); system("pause"); return 0; }
|
总结:
- 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
- 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
- 拥有纯虚析构函数的类也属于抽象类