CC++多态
# C/C++: 多态
# 基本概念
多态分为两类:
- 静态多态:函数重载、运算符重载等,复用函数名;
- 动态多态:派生类和虚函数实现运行时多态。
静态多态与动态多态的区别:
- 静态多态函数地址早绑定——编译阶段确定
- 动态多态函数地址晚绑定——运行阶段确定
# 案例1:谁在说话?
class Animal
{
public:
void speak(){cout<<"动物在说话";}
};
class Cat : public Animal
{
public:
void speak(){cout<<"猫在说话";}
};
此时定义函数让其说话
void doSpeak(Animal &animal)
{
animal.speak();
}
调用函数如下:
void test()
{
Cat cat;
doSpeak(cat);
}
我们创建了子类对象,传入了需要父类形参的函数里,到底是谁在说话?
由于地址早绑定,编译阶段即确定是Animal
,因而没有实现我们的需求。如果想让猫说话,需要函数地址晚绑定,利用虚函数实现。
为父类中的函数添加virtual
关键字即可。
动态多态满足条件
- 有继承关系
- 子类重写父类的虚函数
动态多态的使用
- 父类的指针或者引用,指向子类对象
# 纯虚函数和抽象类
纯虚函数语法:virtual 返回值类型 函数名 (参数列表) = 0;
当类中有了纯虚函数,这个类就称为抽象类,其特点为
- 无法实例化对象
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
# 制作饮品的一个案例
制作饮品的抽象类:
class AbstractDrinking
{
public:
// 煮水
virtual void Boil() = 0;
// 冲泡
virtual void Brew() = 0;
// 加辅料
virtual void PutSth() = 0;
// 制作饮品
void makeDrink(){
Boil();
Brew();
PutSth();
}
}
制作咖啡则可以如下写:
class Coffee :public AbstractDrinking
{
public:
virtual void Boil()
{cout << "煮农夫山泉";}
virtual void Brew()
{cout << "冲咖啡";}
virtual void PutSth()
{cout << "加入牛奶和糖";}
}
写一个制作函数
void DoWork(AbstractDrinking* abs)
{
abs->makeDrink();
delete abs;
}
则可以轻松且清晰地”制作咖啡“
void test()
{
DoWork(new Coffee);
}
# 虚析构和纯虚析构
多态使用过程中,如果有子类属性开辟到堆区,那么父类指针在释放资源时无法调用到子类的析构代码,造成内存泄漏的风险。
解决方案:将父类的析构函数改为虚析构或纯虚析构。
- 虚析构语法:
virtual ~ClassName(){};
- 纯虚析构语法:
virtual ~ClassName() = 0;
- 声明后需要实现
编辑 (opens new window)
上次更新: 2023/08/09, 13:21:24