【C++】C++的多态是个啥,咋用的?

C++的多态是个啥,咋用的?

今天我来讲:多态(Polymorphism)。有人说它是面向对象的灵魂,那么,多态到底是个啥?怎么用?


一、多态是个啥?

"多态"这个词来自希腊语,意思是"多种形态"。放在编程世界里,就是:同一段代码,在不同的对象上,可以表现出不同的行为

举个例子,假设我们有一个 Animal 类,还有两个子类 DogCat

cpp 复制代码
class Animal {
public:
    virtual void speak() {
        std::cout << "动物叫" << std::endl;
    }
};

class Dog : public Animal {
public:
    void speak() override {
        std::cout << "汪汪" << std::endl;
    }
};

class Cat : public Animal {
public:
    void speak() override {
        std::cout << "喵喵" << std::endl;
    }
};

如果写一段代码:

cpp 复制代码
Animal* p1 = new Dog();
Animal* p2 = new Cat();

p1->speak();  // 输出:汪汪
p2->speak();  // 输出:喵喵

你会发现,同样是 speak(),但是不同对象的表现不一样,这就是多态


二、多态分哪几种?

在 C++ 里,多态主要分两类:

  • 编译时多态(静态多态) 例如函数重载、模板。这是在编译期就决定了调用哪个函数。
  • 运行时多态(动态多态) 也就是我们常说的虚函数 + 继承。这是运行期根据对象的真实类型决定调用哪个函数。

今天我们重点聊 运行时多态,因为这是面向对象的核心。


三、多态的三个必要条件

想在 C++ 里实现运行时多态,需要满足三个条件:

  1. 有继承(基类和派生类)
  2. 基类方法是虚函数virtual 关键字)
  3. 通过基类指针或引用调用方法

比如:

cpp 复制代码
Animal* p = new Dog();  // 基类指针指向派生类
p->speak();             // 发生多态

如果不用 virtual,就没有多态,调用的永远是 Animal 里的 speak()


四、底层虚函数表(vtable)!

为什么 C++ 能在运行时决定调用哪个函数? 答案是 虚函数表机制(Virtual Table)

编译器在处理含有虚函数的类时,会做几件事:

  1. 为每个含虚函数的类生成一张 虚函数表(vtable),里面存放该类的虚函数地址。
  2. 在对象里存一个 虚表指针(vptr),指向对应类的虚表。
  3. 当你调用虚函数时,编译器会通过 vptr 查虚表,找到正确的函数地址再执行。

简化后的示意图:

css 复制代码
Animal 对象:
[vptr] -> [Animal::speak]

Dog 对象:
[vptr] -> [Dog::speak]

所以,p->speak() 运行时调用哪一个函数,取决于 vptr 指向哪张虚表。


性能影响大吗?

虚函数调用需要一次额外的指针间接寻址,比普通函数慢一点,但代价很小。

放心用吧,你电脑撑得住~


五、用多态的正确姿势

1. 析构函数要是虚的

如果基类用指针指向派生类对象,那么基类析构函数必须是虚函数,否则会内存泄漏。

cpp 复制代码
class Animal {
public:
    virtual ~Animal() {}  // 一定要 virtual!!!
};

如果不用 virtualdelete p 时只会调用基类析构函数,派生类资源不会释放。


2. 避免 slicing(对象切片)

如果你用值传递,而不是指针或引用,多态会失效。

cpp 复制代码
Dog d;
Animal a = d;  // 切片:只拷贝 Animal 部分
a.speak();     // 输出"动物叫"

3. 多态和模板结合

C++ 的 模板 + 多态 结合非常强大,可以做到编译时多态 + 运行时多态配合,用在策略模式、插件系统等场景。


六、总结~

C++ 的多态就是运行时根据对象的实际类型,动态决定调用哪个方法 ,它通过 虚函数表 实现,是面向对象编程的核心特性之一。

正确使用多态可以让代码更灵活、更易扩展,但也要注意析构、性能和设计合理性。

相关推荐
大龄门外汉3 分钟前
CPP学习之map和set
c++·笔记·学习·stl·set·map·改行学it
追逐时光者9 分钟前
很强!一款基于 .NET 构建、功能强大、通用的 2D 图形编辑器
后端·.net
上官鹿离15 分钟前
C++学习笔记之输入输出流
c++·笔记·学习
ahauedu36 分钟前
30分钟入门实战速成Cursor IDE(2)
ide·ai编程·cursor
mzlogin1 小时前
Java|FreeMarker 复用 layout
java·后端·freemarker
MrHuang9651 小时前
【创建线程的四种方式】
后端
双向331 小时前
Python 多线程日志错乱:logging.Handler 的并发问题
后端
用户992441031561 小时前
从秒杀系统到Serverless:我在分布式架构优化路上踩过的那些坑
后端
AAA修煤气灶刘哥1 小时前
别再懵注解!从 JDK 到 SpringBoot,这篇 “代码贴标签” 攻略超下饭
java·后端·代码规范
IT_陈寒1 小时前
Vite 3.0 性能飞跃的5个关键优化点,让构建速度提升200%!
前端·人工智能·后端