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

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

相关推荐
半桔5 分钟前
【STL源码剖析】从源码看 vector:底层扩容逻辑与内存复用机制
java·开发语言·c++·容器·stl
洛卡卡了17 分钟前
面试官问限流降级,我项目根本没做过,咋办?
后端·面试·架构
千里镜宵烛35 分钟前
互斥锁与条件变量
linux·开发语言·c++·算法·系统架构
ezl1fe36 分钟前
RAG 每日一技(十四):化繁为简,统揽全局——用LangChain构建高级RAG流程
人工智能·后端·算法
amazingCompass43 分钟前
Java 开发必备技能:深入理解与实战 IntelliJ IDEA 中的 VM Options
后端
爱科研的瞌睡虫44 分钟前
C++线程中 detach() 和 join() 的区别
java·c++·算法
欧的曼1 小时前
cygwin+php教程(swoole扩展+redis扩展)
开发语言·redis·后端·mysql·nginx·php·swoole
巴拉巴巴巴拉1 小时前
Spring Boot 整合 Thymeleaf
java·spring boot·后端
凤年徐1 小时前
【数据结构与算法】刷题篇——环形链表的约瑟夫问题
c语言·数据结构·c++·算法·链表
用户1512905452201 小时前
Docker部署 Alist
后端