一.多态的基本语法
c++中的多态分为两类
静态多态:函数重载和运算符重载属于静态多态,复用函数名
动态多态:派生类和虚函数实现运行时多态
静态多态和动态多态区别
静态多态的函数地址早绑定 - 编译阶段确定函数地址
动态多态的函数地址晚绑定 - 运行阶段确定函数地址
我们先来看静态多态的实际例子
#include<iostream>
using namespace std;
class Animal{
public:
void speak(){
cout<< "动物在说话"<<endl;
}
};
class Cat : public Animal{
public:
void speak(){
cout<<"小猫在说话"<<endl;
}
};
void doSpeak(Animal &animal){
animal.speak();
}
int main(){
Cat cat;
doSpeak(cat);
}
这里我们创建了一个doSpeak函数,它相当于父类的引用指向子类的对象
我们来看看运行结果
这里运行的是父类中的speak函数,这是因为静态多态的函数地址早绑定- 编译阶段确定函数地址
如果这时想执行让猫说话,那么这个函数地址就不能提前绑定,需要在运行阶段绑定,需要在运行阶段进行绑定-地址晚绑定
所以我们要进行动态多态
代码如下:
#include<iostream>
using namespace std;
class Animal{
public:
void virtual speak(){
cout<< "动物在说话"<<endl;
}
};
class Cat : public Animal{
public:
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);
}
运行结果:
我们将父类Animal的speak函数定义成虚函数,这样就是动态多态
总结一下动态多态的满足条件:
1.有继承关系
2.子类重写父类的虚函数
3.父类引用指向子类的对象
二.虚析构与纯虚析构
当父类的析构函数不是虚析构函数时,如果通过父类的指针来删除一个派生类的对象,那么将只会调用父类的析构函数,而不会调用派生类的析构函数。这会导致派生类部分的资源(如动态分配的内存、文件句柄、网络连接等)没有被正确释放,从而造成内存泄漏或其他资源泄漏。
#include<iostream>
#include<string>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "这是Animal的构造" << endl;
}
~Animal()
{
cout << "这是Animal的析构" << endl;
}
virtual void speak() = 0;
};
class Cat : public Animal
{
public:
Cat(string name)
{
cout << "这是Cat的构造" << endl;
m_Cat = new string(name);
}
~Cat()
{
cout << "这是Cat的析构" << endl;
}
virtual void speak()
{
cout << *m_Cat << "小猫在说话" <<endl;
}
string *m_Cat;
};
void test01()
{
Animal * cat1 = new Cat("Tom");
cat1->speak();
delete cat1;
}
int main()
{
test01();
}
我们注意这里父类Animal的析构函数不是虚析构
~Animal()
{
cout << "这是Animal的析构" << endl;
}
这里就没有调用Cat的析构函数,最终导致内存泄露。而将Animal的析构函数改为虚析构时,
virtual ~Animal()
{
cout << "这是Animal的析构" << endl;
}
这里就可以正常调用Cat的析构函数