目录
静态多态:函数重载、运算符重载。在编译阶段确定函数地址
动态多态:派生类 虚函数。 在运行阶段确定函数地址
动态多态满足条件:
1 、继承关系
2、子类重写父类虚函数
重写(函数返回值名称、函数类型、返回值列表完全相同)
动态多态的使用
父类的指针或引用 指向子类的对象
动态多态原理
virtual void speak()
{
cout<<"animal speak"<<endl;
}
此时,vfptr(虚函数指针)指向vftable(虚函数表),存放&animal地址
void speak()
{
cout<<"cat speak"<<endl;
}
当子类重写父类函数时,子类的vfptr(虚函数指针)指向vftable(虚函数表),覆盖掉&animal地址,存放&cat地址
void doSpeak(Animal &animal)
{
animal.speak();
}
void test01()
{
Cat cat;
doSpeak(cat);
}
当父类引用指向子类对象时,就会用&cat
总代码
#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;
}
};
void doSpeak(Animal &animal)
{
animal.speak();
}
void test01()
{
Cat cat;
doSpeak(cat);
}
int main()
{
test01();
return 0;
}
纯虚函数和抽象类
有的父类的虚函数本身没意义,只是为了子类重写。那么可以直接纯虚函数
virtual 返回值类型 函数名(参数) = 0;
virtual int getResult() = 0;
类中有纯虚函数,这个类可以成为抽象类
抽象类特点:
1、不能实例化对象
2、子类必须重写抽象类的纯虚函数,否则也属抽象类
虚析构和纯虚析构
子类中没有堆区数据,可以不写虚析构或纯虚析构
虚析构
父类的指针指向子类的对象,在析构时,只会执行父类的析构,不会执行子类的析构。子类有堆区数据,会内存泄漏。
解决方案:虚析构
在基类的析构函数前加 virtual
语法: virtual ~函数名(){}
virtual ~Animal()
{
cout<<"animal 析构"<<endl;
}
纯虚析构
语法:virtual ~类名()=0;
类名::类名(){ }
虚析构是需要实现的,因为要释放父类的指针,换成纯虚析构之后不能在代码段内写实现,应该在类外补一个实现
virtual ~Animal() = 0;
//类外
Animal::~Animal(){}
纯虚析构需要声明也需要实现
有了纯虚析构后,这个类属于抽象类,无法实例化
虚析构和纯虚析构的异同
1、相同:可以解决父类指针释放子类对象,都需要具体的函数实现
2、不同:纯虚析构,该类属于抽象类,无法实例化