(Polymorphism)是面向对象编程的三大特性之一(封装、继承、多态),它让同一个接口在不同对象上表现出不同的行为。
一、什么是多态?
函数调用,在不同对象上产生不同的行为。
class Animal {
public:
virtual void speak() { cout << "动物叫" << endl; }
};
class Dog : public Animal {
public:
void speak() override { cout << "汪汪汪" << endl; }
};
class Cat : public Animal {
public:
void speak() override { cout << "喵喵喵" << endl; }
};
int main() {
Animal* a1 = new Dog();
Animal* a2 = new Cat();
a1->speak(); // 输出:汪汪汪
a2->speak(); // 输出:喵喵喵
delete a1;
delete a2;
}
表面上都是调用 Animal::speak(),但结果却不同-这就是多态。
C++中的多态类型
C++的多态分为两种:

三、运行时多态的三个条件:
C++实现运行时多态必须满足三个条件:
基类中定义虚函数(virtual )
派生类重写该虚函数
3.通过基类指针或引用调用该虚函数
四、底层实现机制(虚函数表 vtable)
编译器为含有虚函数的类自动生成:
每个虚函数表(vtable):存放各个虚函数的函数指针;每个对象中都有一个 虚表指针(vptr),指向类的虚表。
Animal对象
+----------------+
| vptr ----------|------> [ Animal vtable ]
+----------------+ +---------------------+
| speak() -> Animal::speak |
+---------------------+
Dog对象
+----------------+
| vptr ----------|------> [ Dog vtable ]
+----------------+ +---------------------+
| speak() -> Dog::speak |
+---------------------+
当你执行 a1->speak()时,程序会
1.根据对象a1 找到它的Vptr;
2.通过 vptr 找到它的 vtable;
3.在虚表中查找 speak()的函数指针;
4.调用实际函数(Dog::speak()或cat::speak())
因此:
动态绑定是在运行时完成的;调用哪个函数取决于对象的实际类型。
五、关键细节
(1)为什么析构函数常写成虚函数?
因为:
Animal* p = new Dog();
delete p;
如果基类析构函数不是虚函数,则只会调用 Animal::~Animal(),不会调用 Dog::~Dog(),
会造成内存泄漏。
(2)纯虚函数与抽象类
如果基类只定义接口,不实现:
class Animal {
public:
virtual void speak() = 0; // 纯虚函数
};
这种类称为 抽象类,不能实例化,只能作为接口或父类。
(3)虚表只生成一份
虚函数表是类级别的静态结构,而不是对象独有的。
六、总结对比
特性 静态多态 动态多态

一句话总结
C++多态让同一个接口(函数调用)在不同对象上表现出不同的行为静态多态靠编译器决议(重载、模板),动态多态靠运行时虚表机制(virtual)。

二、为什么要引入抽象基类和纯虚函数?
一、先看背景:为什么不够用?
假设我们写一个程序,要管理不同的动物:
class Dog {
public:
void speak() { cout << "汪汪汪" << endl; }
};
class Cat {
public:
void speak() { cout << "喵喵喵" << endl; }
};
如果要写一个“让动物叫”的函数
void makeSound(Dog d) { d.speak(); }
void makeSound(Cat c) { c.speak(); }
问题
每新增一种动物,都要重载一次 makesound();
不同动物间缺少统一接口,无法通过一个“通用指针“来操作。
引出解决思路:抽象出共性
我们发现所有动物都会“叫”,于是可以提取一个公共接口:
class Animal {
public:
virtual void speak() = 0; // 纯虚函数(只定义行为,不给实现)
};
这里=0表示纯虚函数类中只要存在纯虚函数,这个类就是 抽象基类(abstract class),不能被实例化。
三、派生类负责具体实现
class Dog : public Animal {
public:
void speak() override { cout << "汪汪汪" << endl; }
};
class Cat : public Animal {
public:
void speak() override { cout << "喵喵喵" << endl; }
};
然后我们就可以这样写:
void makeSound(Animal* p) {
p->speak(); // 多态调用
}
int main() {
Dog d;
Cat c;
makeSound(&d);
makeSound(&c);
}
汪汪汪
喵喵喵
此时:
Animal 只定义了"会叫“的抽象概念,具体“怎么叫”,交由子类决定;makesound()不再关心具体类型。
四、核心作用总结
目的 说明

五、类比理解
可以把 抽象基类 + 纯虚函数 理解为:
"接口定义者”+“实现者
就像:
Animal :制定规则的人(所有动物都得能叫)cat :按规则提供自己实现的参与者DOg、
一句话总结
纯虚函数 是定义“接口“的方式
抽象基类 是这些接口的载体
它们让 C++的多态真正具备扩展性与统一性,是大型系统设计的基础。
547

被折叠的 条评论
为什么被折叠?



