news 2026/4/17 2:27:47

C++怎么实现多态

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++怎么实现多态

多态指同一个“接口”或“调用语句”,在不同类型上表现出不同的行为。C++中主要有两种多态,静态多态和动态多态

静态多态

函数在被调用时,具体执行哪段代码,在编译阶段就已经确定了。编译器根据函数参数的类型或数量,直接把函数调用链接到具体的函数地址,无需继承和虚函数。

实现方式
  1. 函数重载
  2. 运算符重载
  3. 模板
代码示例

函数重载:

#include <iostream> void print(int i) { std::cout << "打印整数: " << i << '\n'; } void print(double d) { std::cout << "打印浮点数: " << d << '\n'; } int main() { // 编译器根据参数是int,绑定到 print(int) 的地址 print(10); // 编译器根据参数是double,绑定到 print(double) 的地址 print(3.14); return0; }

运算符重载:

#include <iostream> class Point { private: int x, y; public: Point(int x = 0, int y = 0) : x(x), y(y) {} // 重载 + 运算符 Point operator+(const Point& other) const { return Point(this->x + other.x, this->y + other.y); } // 友元函数重载 << friendstd::ostream& operator<<(std::ostream& os, const Point& p); }; // 全局重载 << std::ostream& operator<<(std::ostream& os, const Point& p) { os << "(" << p.x << ", " << p.y << ")"; return os; } int main() { Point p1(10, 20); Point p2(5, 5); // Point + Point, 调用 Point::operator+(const Point&) Point p3 = p1 + p2; std::cout << "p3: " << p3 << std::endl; // 输出: (15, 25) return0; }

模版:

#include <iostream> template <typename T> void add(T a, T b) { std::cout << "模板加法结果: " << a + b << std::endl; } int main() { // 编译器根据参数类型是int,自动生成 add<int> 版本 add(1, 2); return 0; }
优缺点

优点:编译器完成方法绑定,编译器可进行内联优化;不依赖继承体系,组合更灵活。

缺点:必须在编译时就知道所有类型;编译耗时、代码膨胀。

动态多态

函数在被调用时,具体执行哪段代码,在编译阶段无法确定,只有在程序运行时,根据对象的实际类型(是基类对象还是派生类对象)来决定。

实现方式

三条必须满足,缺一不可:

  1. 有继承关系

  2. 基类中有虚函数,派生类重写了该虚函数

  3. 通过基类的指针或引用去调用虚函数

#include <iostream> // 基类 class Shape { public: // 虚函数 virtual void draw() { std::cout << "绘制一个通用的形状" << std::endl; } }; // 派生类 class Circle :public Shape { public: // 重写基类虚函数 void draw() override { std::cout << "绘制一个圆形 O" << std::endl; } }; // 派生类 class Rectangle :public Shape { public: // 重写基类虚函数 void draw() override { std::cout << "绘制一个矩形 []" << std::endl; } }; // 注意这里函数参数是Shape*,编译时不知道具体指向哪个类型 // 只有运行时才知道指针指向的具体类型 void startDrawing(Shape* shape) { shape->draw(); } int main() { Circle c; Rectangle r; startDrawing(&c); // 输出:绘制一个圆形 O startDrawing(&r); // 输出:绘制一个矩形 [] return0; }
实现原理

在编译含有 virtual 函数的类时,编译器会自动添加虚函数表虚指针,通过查找虚函数表得到实际要调用函数的地址

虚函数表(vtable):

  1. 归属于类,同一个类共享一个虚函数表。
  2. 是一个函数指针数组,存放该类所有虚函数地址。
  3. 存储在只读数据段 .rodata,程序运行时加载

虚指针(vptr):

  1. 归属于对象,每个对象都有一个虚指针。
  2. 是一个指针,指向该类的vtable。
  3. 存储在对象内存布局的最头部,便于快速读取。

假设有一个基类 Base,含有两个虚函数 func1 和 func2,Derived 类继承自 Base,并重写了 func1 函数,func2 函数则继承了 Base 的 func2:

class Base { public: virtual void func1() { cout << "Base::func1" << endl; } // 虚函数 virtual void func2() { cout << "Base::func2" << endl; } // 虚函数 virtual ~Base() = default; // 虚析构函数 void normal() {} // 普通函数,不占对象内存 private: int a; int b; }; class Derived :public Base { public: // 重写了 func1 void func1() override { cout << "Derived::func1" << endl; } // func2继承了Base的func2 private: int c; int d; };
优缺点

1.优点:灵活性高,可以通过基类指针或引用操作派生类对象。

2.缺点:编译器无法优化虚函数;运行时进行类型检查和方法绑定,有一定开销。

虚函数是怎么实现动态绑定的?vptr 和 vtable是什么?

有虚函数的类中会有一个隐藏指针 vptr,指向该类的虚函数表 vtable,vtable 是一个函数指针数组,存放该类所有虚函数地址。调用虚函数时,编译器生成代码从对象取 vptr,再在 vtable 的槽位中找到函数地址并间接调用,从而在运行期决定调用哪个版本。

一个对象里有几个 vptr?

单继承通常只有一个。多继承时,每个含虚函数的基类子对象各自含有一个 vptr。

普通成员函数占对象内存吗?

不占。成员函数代码存储在可执行文件的代码段,每个对象只存数据成员和编译器需要的字段

基类有一个 func(int),派生类写 func(double),会怎样?

会触发名字隐藏,派生类的 func(double) 会把基类的所有同名函数隐藏掉,导致无法通过派生类对象调用基类的 func(int)

为什么基类析构函数要设为 virtual?

通过基类指针删除派生类对象时,如果基类析构函数非 virtual,会导致只调用基类的析构函数,而不调用派生类的析构函数,导致资源泄露

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 2:25:42

第一阶段:Java入门基础②

第一阶段&#xff1a;Java入门基础 | ⭐ JDK安装与环境配置 - 手把手教学指南 &#x1f4c5; 更新时间&#xff1a;2026年4月16日 &#x1f3af; 学习阶段&#xff1a;第一阶段&#xff1a;Java入门基础 ⏱️ 建议用时&#xff1a;1天 &#x1f4cc; 阶段目标&#xff1a;掌握J…

作者头像 李华
网站建设 2026/4/17 2:23:19

CloudCompare点云配准实战:从手动对点到多视角融合

1. 点云配准入门&#xff1a;为什么需要手动对齐&#xff1f; 第一次接触点云配准时&#xff0c;我完全被那些密密麻麻的彩色点搞懵了。直到用CloudCompare手动对齐了两个扫描视角的数据&#xff0c;才真正理解这个过程的必要性。简单来说&#xff0c;点云配准就像玩拼图——当…

作者头像 李华
网站建设 2026/4/17 2:23:16

AI新闻摘要生成技术白皮书(SITS2026核心算法解密)

第一章&#xff1a;AI新闻摘要生成技术白皮书&#xff08;SITS2026核心算法解密&#xff09; 2026奇点智能技术大会(https://ml-summit.org) SITS2026核心算法是面向高时效、多源异构新闻流设计的端到端摘要生成框架&#xff0c;融合动态语义压缩&#xff08;DSC&#xff09;…

作者头像 李华
网站建设 2026/4/17 2:22:14

如何应用Claude编程及中转站选择

首先推荐两个网站 一个是cc switch网站 这是快捷切换中转站的工具 https://github.com/farion1231/cc-switch大家尽量在这下面的网站上选取中转站一个是中转站的测评网站 https://www.helpaio.com/transitClaude Code 使用 VS Code 设置全局环境变量 ANTHROPIC_BASE_URL&#x…

作者头像 李华