多态分类
静态多态
运算符重载 函数重载都属于静态多态 复用函数名
动态多态
在虚函数和派生类之间实现运行时多态
多态特点
静态多态
函数地址早绑定 编译阶段就确定好了函数的入口地址
动态多态
函数地址晚绑定 运行阶段才能确定好函数的入口地址
为什么要有多态
通过以下例子
class Animal
{
public:
void speak()
{
cout << "动物在说话\n";
}
int p;
};
class Cat : public Animal
{
public:
void speak()
{
cout << "小猫在说话\n";
}
};
class Dog : public Animal
{
public:
void speak()
{
cout << "小狗在说话\n";
}
};
很简单的一段代码 有一个基类Animal
两个派生类Dog
Cat
公共继承Animal
我创建一个Cat
类的对象neko
然后使用Animal
指针指向它
Cat neko;
Animal* animal = &neko;
那么大家猜一下这个会输出什么
animal->speak();
很多人会猜输出小猫在说话
毕竟我们创建的是Cat
的数据类型
但是答案偏偏是 动物在说话
为啥呢
这是因为地址早绑定
在编译阶段就确定了运行引用类型中定义的函数
我们需要一种方法将其改为地址晚绑定
在运行阶段才能确定运行引用类型还是实际类型中的函数,根据实际类型确定而不是引用类型
虚函数
还是刚才那个例子
我们只需要将父类函数前加上virtual 使其变成虚函数 就能实现地址晚绑定
virtual void speak()
{
......
}
这样编译器只能在运行时 根据传入的数据类型确定调用谁的函数
疑惑
有些人可能会问 我直接在子类重写一个函数把父类覆盖掉就好嘞呗 为啥还要这么麻烦 用父类指针指向子类对象
直接创建子类对象也可以实现类似操作 我也不知道为啥 慢慢深入吧
多态条件
发生继承
子类重写父类虚函数
注意这地方
重写和重载不一样
重写的参数列表必须相同
重载的参数列表可以不一样