- 基本概念
- 动态多态的满足条件
- 动态多态的使用
- 动态多态的原理浅析
- 纯虚函数和抽象类
- 虚析构和纯虚析构
多态分为两种
- 静态多态
函数重载和运算符重载属于静态多态,复用函数名。编译时确定函数地址
- 动态多态
用子类和虚函数实现运行时多态,运行时确定函数地址。
父类的指针或引用指向子类的对象,根据子类的不同,来确定调用的具体是谁的函数。
//动态多态 class Animal { public: virtual void speak(){ cout << "动物在说话" << endl; } }; class Cat: public Animal { public: void speak(){ //也可以写成virtual void speak(){} cout << "小猫在说话" << endl; } }; class Dog: public Animal { public: void speak() { cout << "小狗在说话" << endl; } }; void doSpeak(Animal& animal) { animal.speak(); } int main(){ Cat cat; doSpeak(cat); //小猫在说话 Dog dog; doSpeak(dog); //小狗在说话 return 0; }动态多态的满足条件
- 有继承关系
- 子类重写父类的虚函数
父类的指针或引用指向子类的对象
动态多态的原理浅析//class Animal { //public: // void speak(){ // cout << "动物在说话" << endl; // } //}; //count << sizeof(Animal); //1,相当于一个空类,大小为1个字节 class Animal { public: virtual void speak(){ cout << "动物在说话" << endl; } }; count << sizeof(Animal); //4,即一个指针的大小 //此Animal类包含了一个vfptr指针,指向这个类的虚函数表vftable,表内记录了此类的虚函数的地址,比如&Animal::speak //vfptr - virtual function pointer 虚函数(表)指针 //class Dog: public Animal {}; //如果子类不重写父类的虚函数,那么子类的虚函数表里的内容将和父类的一样,也是&Animal::speak class Dog: public Animal { public: void speak() { //如果子类重写了虚函数,那么虚函数表中的父类虚函数地址将会被替换为子类的函数地址 cout << "小狗在说话" << endl; } };纯虚函数和抽象类
在多态中,父类中的虚函数的实现通常是无意义的,主要都是调用子类重写的内容
基于此,我们可以把虚函数改写为纯虚函数
//纯虚函数格式: //virtual 返回值类型 函数名(参数列表) = 0; class Animal { public: virtual void speak() = 0; };
包含纯虚函数的类称为抽象类
抽象类无法实例化对象
子类要继承抽象类,必须重写它的纯虚函数,除非子类也是一个抽象类
虚析构和纯虚析构使用多态时,如果子类中有成员变量开辟在堆区,由于父类对象析构时,不会调用子类的析构函数,会造成堆区内存泄漏的问题
- 解决方案
将父类中的析构函数改为虚析构或纯虚析构,就会自动调用子类的析构函数
-
两者共性
- 都能自动析构子类对象
- 都需要具体的函数实现
-
两者区别
- 如果一个类中包含纯虚析构,则该类属于抽象类,无法实例化对象
-
语法
//虚析构 virtual ~类名(){ //... }; //纯虚析构 virtual ~类名() = 0; //必须要在类外实现 类名::~类名(){ //... }
子类中如果没有堆区数据,可以不写虚析构和纯虚析构
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)