当前位置: 首页 > news >正文

C++ <多态>详解:从概念到底层实现

目录

1. 多态的概念

2. 多态的定义及实现

2.1 多态的构成条件

2.2 虚函数与重写

特殊重写场景:

2.3 关键字:override 与 final

3. 纯虚函数与抽象类

4. 多态的底层原理

4.1 虚函数表指针(_vfptr)

4.2 动态绑定与静态绑定

5. 常见面试题解析

6. 总结


多态是 C++ 面向对象编程的核心特性之一,它允许不同对象对同一消息作出不同响应。理解多态不仅能写出更灵活的代码,也是面试中的高频考点。本文将从概念、实现条件、关键特性到底层原理,全面剖析 C++ 多态的精髓。

1. 多态的概念

多态(polymorphism)字面意思是 "多种形态",在 C++ 中分为两类:

  • 编译时多态:通过函数重载和函数模板实现,编译器在编译阶段根据参数类型匹配相应函数,属于静态多态。
  • 运行时多态:程序运行时根据传递的对象类型决定调用哪个函数,这是本文的重点。

运行时多态的经典案例

  • 买票行为:普通人全价、学生半价、军人优先
  • 动物叫行为:猫 "喵"、狗 "汪"
//买票的多态实例
class Person
{
public:virtual void BuyTicket(){cout << "买票-全价" << endl;}
};class Student : public Person
{
public:virtual void BuyTicket()   //可以不加virtual 不规范{cout << "买票-半价" << endl;}
};void Func(Person& ptr) //基类的引用或指针
{ptr.BuyTicket();
}// 动物叫的多态示例
class Animal {
public:virtual void talk() const {}
};
class Dog : public Animal {
public:virtual void talk() const { cout << "汪汪" << endl; }
};
class Cat : public Animal {
public:virtual void talk() const { cout << "喵" << endl; }
};
// 同一函数接收不同对象,表现不同行为
void letsHear(const Animal& animal) {animal.talk(); 
}

 

2. 多态的定义及实现

2.1 多态的构成条件

多态需满足继承关系基础上的两个核心条件:

class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:// 重写基类虚函数virtual void BuyTicket() { cout << "买票-半价" << endl; }
};
// 基类引用调用虚函数,满足多态条件
void Func(Person& people) {people.BuyTicket(); 
}
int main() {Person p;Student s;Func(p); // 输出"买票-全价"Func(s); // 输出"买票-半价"
}

 

2.2 虚函数与重写

  • 虚函数:用virtual修饰的类成员函数(非成员函数不能用virtual)。
  • 重写规则:派生类虚函数需与基类虚函数的函数名、参数列表、返回值完全一致(协变除外)。

注意:派生类重写时可省略virtual(不推荐),但仍视为虚函数。

特殊重写场景:
  1. 协变:返回值可改为基类 / 派生类的指针 / 引用(需满足继承关系)

    class A {};
    class B : public A {};
    class Person {
    public:virtual A* BuyTicket() { return nullptr; } // 基类指针
    };
    class Student : public Person {
    public:virtual B* BuyTicket() { return nullptr; } // 派生类指针(协变)
    };

    析构函数重写:基类析构函数为虚函数时,派生类析构函数自动视为重写(编译器统一处理析构函数名为destructor

    class A {
    public:virtual ~A() { cout << "~A()" << endl; } // 虚析构
    };
    class B : public A {
    public:~B() { cout << "~B()" << endl; } // 自动重写
    };
    // 正确释放派生类资源,避免内存泄漏
    int main() {A* p = new B;delete p; // 先调用~B(),再调用~A()
    }

    2.3 关键字:override 与 final

  • override:强制检查派生类函数是否重写基类虚函数,未重写则编译报错。
  • final:修饰虚函数时,禁止派生类重写该函数;修饰类时,禁止被继承。
    // C++11提供了override,可以帮助⽤⼾检测是否重写。
    class Car
    {
    public:virtual Dirve(){}
    };class Benz : public Car
    {
    public:virtual Dirve() override{cout << "Benz-舒适" << endl;}
    };int main()
    {return 0;
    }
    
    class Car {
    public:virtual void Drive() final {} // 禁止重写
    };
    class Benz : public Car {
    public:virtual void Drive() {} // 编译报错:无法重写final函数
    };

    3. 纯虚函数与抽象类

  • 纯虚函数:在虚函数后加=0,无需实现(语法允许实现但无意义)。
  • 抽象类:包含纯虚函数的类,不能实例化对象,派生类需重写纯虚函数才能实例化。
    class Car {
    public:virtual void Drive() = 0; // 纯虚函数
    };
    class Benz : public Car {
    public:virtual void Drive() { cout << "Benz-舒适" << endl; } // 重写后可实例化
    };
    int main() {// Car car; // 报错:抽象类不能实例化Car* p = new Benz; // 正确p->Drive();
    }

    4. 多态的底层原理

    4.1 虚函数表指针(_vfptr)

    含有虚函数的类,其对象会额外包含一个虚函数表指针_vfptr),指向虚函数表(简称虚表)。虚表是存储虚函数地址的指针数组,同类型对象共享同一张虚表。

class Base {
public:virtual void Func1() { cout << "Func1()" << endl; }
protected:int _b = 1;char _ch = 'x';
};
int main() {Base b;cout << sizeof(b) << endl; // 32位下为12字节(4字节指针 + 4字节int + 4字节对齐)
}

 

4.2 动态绑定与静态绑定

  • 静态绑定:编译时确定函数调用地址(非虚函数或不满足多态条件时)。
  • 动态绑定:运行时通过对象的虚表查找函数地址(满足多态条件时)。

虚表结构示例

  • 基类虚表:存储基类所有虚函数地址。
  • 派生类虚表:先复制基类虚表,再用派生类重写的虚函数地址覆盖对应位置,最后添加派生类自身的虚函数地址。

5. 常见面试题解析

问题:以下程序输出结果是?

class A {
public:virtual void func(int val = 1) { cout << "A->" << val << endl; }virtual void test() { func(); }
};
class B : public A {
public:void func(int val = 0) { cout << "B->" << val << endl; }
};
int main() {B* p = new B;p->test(); // 输出:B->1
}

解析

  • test()是基类函数,调用func()时满足多态,实际调用派生类B::func
  • 虚函数重写仅覆盖函数体(即函数的实现),默认参数仍使用基类的(val=1),故输出B->1

6. 总结

多态是 C++ 面向对象的核心特性,通过虚函数重写虚表机制实现,允许同一接口呈现不同行为。关键要点:

  1. 运行时多态需满足:基类指针 / 引用 + 虚函数重写。
  2. 虚表存储虚函数地址,派生类虚表覆盖基类重写函数地址。
  3. 基类析构函数建议设为虚函数,避免派生类资源泄漏。
  4. 抽象类通过纯虚函数强制派生类实现特定接口,提升代码规范性。

掌握多态不仅能写出更灵活的代码,也是理解 C++ 底层机制的重要一步。实际开发中,合理使用多态可显著提高代码的可扩展性和维护性。

 

http://www.dtcms.com/a/295505.html

相关文章:

  • 不同头会关注输入序列中不同的部分和不同维度所蕴含的信息,这里的头和嵌入维度不是对应的,仅仅是概念上的吗?
  • 在Ubuntu上使用QEMU学习RISC-V程序(1)起步第一个程序
  • 负载均衡-LoadBalance
  • YOLOv4深度解析:革命性的实时目标检测技术
  • 基于Zig语言,opencv相关的c++程序静态交叉编译
  • USRP X440
  • Vulnhub Web-Machine-N7靶机攻略(附VB安装教程)
  • Docker快速安装Clickhouse
  • Vue 项目中的组件引用如何实现,依赖组件间的数据功能交互及示例演示
  • OpenLayers 综合案例-基础图层控制
  • 解密 Base64 编码:从原理到应用的全面解析
  • 前端实现 excel 数据导出,封装方法支持一次导出多个Sheet
  • Effective Python 第16条:用get处理字典缺失键,避免in与KeyError的陷阱
  • 时间日期选择器组件进行日期和时间的禁用处理逻辑
  • 让UV管理一切!!!
  • wiz2025 挑战赛从 SpringActuator 泄露到 s3 敏感文件获取全解析
  • 再生基因总结
  • Vue工程化 ElementPlus
  • Android Camera createCaptureSession
  • 精密圆柱销类分拣系统“cad【9张】三维图+设计书明说
  • 货车手机远程启动的扩展功能有哪些
  • 二次元姓名生成器(饮料名+动漫角色名)
  • 研发过程都有哪些
  • 遨游三防平板|国产芯片鸿蒙系统单北斗三防平板,安全高效
  • 【jupyter 使用多进程方案】
  • 使用爬虫获取游戏的iframe地址
  • SSL 证书与 HTTPS 的关系:一文理清核心关联
  • 顶级水体视效一键添加~地表中的水体设置
  • OpenCV计算机视觉实战(17)——特征点检测详解
  • 基于python django的农业可视化系统,以奶牛牧场为例