写点什么

C++ 多态:你只要这么讲就够了!

作者:岭南过客
  • 2025-10-30
    广东
  • 本文字数:1980 字

    阅读完需:约 6 分钟

C++ 多态:你只要这么讲就够了!

概念

多态说白了就是"一个接口,能玩出多种花样"。  

在 C++里,多态主要分两种:一种是编译时就能决定的静态多态,另一种是运行时才确定的动态多态。  


这两者的关键区别就在于——调用函数的时候,系统到底是在编译阶段就定好要执行哪个函数,还是等到程序跑起来时才临时决定。


静态多态(编译时多态)

静态多态是指在编译阶段就确定函数调用的具体实现,不需要虚函数或继承,主要通过以下两种方式实现:


1. 函数重载(Function Overloading)

在同一作用域内,允许定义多个同名函数,但它们的参数列表(参数类型、个数或顺序)必须不同。编译器会根据实参的类型和数量,在编译时确定调用哪个函数。

示例
#include <iostream>using namespace std;// 重载函数:参数类型不同int add(int a, int b){    return a + b;}double add(double a, double b){    return a + b;}int main(){    // 编译时确定调用int版本,输出5    cout << add(2, 3) << endl;   
// 编译时确定调用double版本,输出6.0 cout << add(2.5, 3.5) << endl; return 0;}
复制代码

2. 运算符重载(Operator Overloading)

允许对内置运算符(如+、-、<<等)进行重新定义,使其能作用于自定义类型。编译器会根据操作数的类型,在编译时确定调用哪个重载版本。

示例
#include <iostream>using namespace std;class Point{public:    Point(int x = 0, int y = 0)        : x(x), y(y) {}    // 重载+运算符    Point operator+(const Point& other) const    {        return Point(x + other.x, y + other.y);    }    // 重载<<运算符(全局函数)    friend ostream& operator<<(ostream& os, const Point& p)    {        os << "(" << p.x << "," << p.y << ")";        return os;    }
private: int x, y;};
int main(){ Point p1(1, 2), p2(3, 4); // 编译时确定调用Point::operator+ Point p3 = p1 + p2; // 编译时确定调用全局operator<<,输出(4,6) cout << p3 << endl; return 0;}
复制代码

运行时多态

动态多态是指在程序运行阶段才确定函数调用的具体实现,是面向对象编程中 “多态” 的核心体现,其实现依赖于以下三个条件:

● 存在继承关系(基类与派生类);

● 基类中声明虚函数(virtual 关键字),派生类重写(override)该虚函数;

● 通过基类的指针或引用调用虚函数。

示例
#include <iostream>using namespace std;// 基类class Animal{public:    // 虚函数    virtual void bark()    {        cout << "动物在叫" << endl;    }};// 派生类:狗class Dog : public Animal{public:    // 重写基类虚函数    void bark() override    {        cout << "汪汪汪" << endl;    }};// 派生类:猫class Cat : public Animal{public:    // 重写基类虚函数    void bark() override    {        cout << "喵喵喵" << endl;    }};// 通过基类引用调用虚函数void letItBark(Animal& animal){    // 运行时根据实际对象类型确定调用哪个函数    animal.bark(); }int main(){    Dog dog;    Cat cat;    // 运行时确定调用Dog::bark(),输出"汪汪汪"    letItBark(dog);    // 运行时确定调用Cat::bark(),输出"喵喵喵"    letItBark(cat);    return 0;}
复制代码

运行时多态的底层实现:虚函数表(vtable)

1. 每个包含虚函数的类(或其派生类)都会有一个虚函数表,存储该类所有虚函数的地址。

2. 类的每个对象会包含一个虚表指针(vptr),指向该类的虚函数表。

3. 当通过基类指针 / 引用调用虚函数时,编译器通过 vptr 找到对应类的 vtable,再调用表中实际的函数地址(运行时绑定)。

通过汇编看运行时多态的原理(执行过程)

通过下面的指令生成上述运行时多态示例的汇编代码

g++ -S -g -O0 -fverbose-asm  -masm=intel  temp.cpp -o temp.s
复制代码

虚函数表内存布局


注:每个包含虚函数的类都已自己的虚函数表。


设置虚函数表


注:类对象实例化时会为此对象设置虚函数表地址。


运行时多态调用


注:运行时多态就是通过类对象实例化时设置的虚函数表保存的实际的函数地址进行函数调用的。

多态的作用

1. 代码复用:通过基类统一接口,派生类实现具体功能,减少重复代码。

2. 扩展性:新增派生类时,无需修改使用基类接口的代码(如示例中的 letItBark),符合 “开闭原则”。

3. 灵活性:同一操作可适配不同对象,提高代码的通用性。

结语

● 静态多态:编译时绑定,通过函数重载或运算符重载实现,不依赖虚函数和继承,效率高但灵活性低。

● 动态多态:运行时绑定,通过虚函数、继承和基类指针 / 引用实现,灵活性高(符合 “开闭原则”),是面向对象设计中多态的核心应用。


扩展阅读(复杂指针的拆解方法):https://mp.weixin.qq.com/s/rPzZqNjGcq4gQx8FF_bcsg?token=1073149679&lang=zh_CN

发布于: 刚刚阅读数: 3
用户头像

岭南过客

关注

公*号 os-artificer 技术圈 的 作者 2021-08-08 加入

💪世界五百强企业资深码农🤪(十多年了)✌🏽

评论

发布
暂无评论
C++ 多态:你只要这么讲就够了!_岭南过客_InfoQ写作社区