1. 多态的基本概念
多态分为两类:
- 静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名
- 动态多态: 派生类和虚函数实现运行时多态
动态多态满足的条件:
- 有继承关系
- 子类重写父类的虚函数
- 父类的指针或者引用指向子类对象
函数返回值类型 函数名 参数列表 完全一致称为重写
#include <iostream>
using namespace std;
class Animal
{
public:
virtual void speak()
{
cout << "动物在说话" << endl;
}
};
class Cat : public Animal
{
public:
void speak()
{
cout << "小猫在说话" << endl;
}
};
class Dog : public Animal
{
public:
void speak()
{
cout << "小狗在说话" << endl;
}
};
// 传入派生类会自动转换为基类,输出“动物在说话”
// 将基类中说话函数变为 virtual 虚函数,可以输出派生类“xx在说话”
// 父类的指针或者引用指向子类对象 Animal & animal = cat;
void DoSpeak(Animal &animal)
{
animal.speak();
}
void test01()
{
Cat cat;
DoSpeak(cat);
Dog dog;
DoSpeak(dog);
}
int main()
{
test01();
system("pause");
return 0;
}
如果使用指针的写法:
void DoSpeak(Animal *animal)
{
animal->speak();
}
void test01()
{
Animal * cat = new Cat;
DoSpeak(cat);
delete cat;
}
2. 纯虚函数和抽象类
在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容。因此可以将虚函数改为==纯虚函数==。
纯虚函数语法:virtual 返回值类型 函数名(参数列表) = 0 ;
当类中有了纯虚函数,这个类也称为==抽象类==。
抽象类特点:
- 无法实例化对象
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
#include <iostream>
using namespace std;
class Base {
public:
virtual void func() = 0;
};
class Son : public Base {
public:
// 子类中的 virtual 写不写都可以
virtual void func() {
cout << "Son::func()" << endl;
}
};
int main() {
Base *base = NULL;
// 抽象类无法实例化 base = new Base; 是不对的
base = new Son;
base->func();
delete base;
return 0;
}
3. 虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。解决方式:将父类中的析构函数改为虚析构或者纯虚析构。
虚析构和纯虚析构共性:
- 可以解决通过父类指针释放子类对象
- 都需要有具体的函数实现
虚析构和纯虚析构区别:
- 如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法:virtual ~类名(){}
纯虚析构语法:virtual ~类名() = 0; 或者 类名::~类名(){}
#include <iostream>
using namespace std;
#include <string>
class Animal
{
public:
Animal()
{
cout << "Animal 构造函数调用!" << endl;
}
virtual void speak() = 0;
~Animal()
{
cout << "Animal虚析构函数调用!" << endl;
}
};
class Cat : public Animal
{
public:
Cat(string name)
{
cout << "Cat构造函数调用!" << endl;
m_Name = new string(name);
}
virtual void speak()
{
cout << *m_Name << "小猫在说话!" << endl;
}
~Cat()
{
cout << "Cat析构函数调用!" << endl;
if (m_Name != NULL)
{
delete m_Name;
m_Name = NULL;
}
}
// 将 m_name 创建到堆区
string *m_Name;
};
int main()
{
Animal *animal = new Cat("TOM");
animal->speak();
delete animal;
return 0;
}
发现没有执行Cat析构函数调用!说明没有释放内存,导致内存泄露。
解决方法:将父类的析构改为虚析构。
virtual ~Animal()
{
cout << "Animal虚析构函数调用!" << endl;
}
如果使用纯虚析构:(也需要加上具体实现)(纯虚函数只需声明就可以)
class Animal
{
public:
Animal()
{
cout << "Animal 构造函数调用!" << endl;
}
virtual void speak() = 0;
virtual ~Animal() = 0;
};
Animal::~Animal()
{
cout << "Animal析构函数调用!" << endl;
}
有了纯虚析构之后,这个类也属于抽象类,无法实例化对象。