多态
用通俗的话解释,多态就是一种事物在不同的情形下具备不同形态,方法的重载也是一种多态。
多态分为两类:1)静态多态: 函数地址早绑定,编译器确定函数地址,如函数重载、运算符重载;2)动态多态:函数地址晚绑定,在运行阶段确定函数地址,通过派生类和虚函数实现。
#include<iostream>
using namespace std;
class Animal {
public:
// virtual声明虚函数,在编译期间不能确定函数调用
virtual void speak() {
cout << "动物在说话" << endl;
}
};
class Dog : public 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;
Dog dog;
cout << "猫开始调用" << endl;
doSpeak(cat);
}
多态:父类的引用指向子类对象!
结构图示意:
class Animal {
void speak() { cout << "animal speak;" << endl; }
}
int main() {
cout << "sizeof(Animal):" << sizeof(Animal) << endl; // 1
}
// Animal只有一个非静态成员函数,不属于类。空类占内存大小为1 字节
// 但当Animal类中有虚函数时,占用内存大小为4 字节
纯虚函数和抽象类
多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将虚函数改为纯虚函数,包含纯虚函数的类为抽象类
virtual 返回值类型 函数名 (参数列表)= 0 ;
抽象类特点:
- 无法实例化对象
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
class Animal {
public:
virtual void speak()=0;
};
class Dog : Animal {
public:
void speak() {cout << "dog speak" << endl;}
};
虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:
- 可以解决父类指针释放子类对象
- 都需要有具体的函数实现
虚析构和纯虚析构区别:
- 如果是纯虚析构,该类属于抽象类,无法实例化对象
class Animal {
public:
Animal()
{
cout << "Animal 构造函数调用!" << endl;
}
virtual void Speak() = 0;
//析构函数加上virtual关键字,变成虚析构函数
//virtual ~Animal()
//{
// cout << "Animal虚析构函数调用!" << endl;
//}
virtual ~Animal() = 0;
};
Animal::~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 (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;
}