当前位置: 首页 > 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.lryc.cn/news/598572.html

相关文章:

  • Java 实现 B/S 架构详解:从基础到实战,彻底掌握浏览器/服务器编程
  • 深入理解 ThreadLocal:从原理到最佳实践
  • LLM层归一化:γβ与均值方差的协同奥秘
  • MySQL--day13--视图存储过程与函数
  • 【小董谈前端】【样式】 CSS与样式库:从实现工具到设计思维的跨越
  • 大数据集分页优化:LIMIT OFFSET的替代方案
  • MySQL数据库迁移至国产数据库测试案例
  • multiprocessing模块使用方法(二)
  • 微信格式插件 建的文件位置
  • 负载均衡-LoadBalance
  • 机器学习基础-k 近邻算法(从辨别水果开始)
  • TCP重传率优化在云服务器网络协议栈的调优实践
  • Java面试宝典:Spring专题二
  • openbmc 日志系统继续分析
  • 科大讯飞运维 OceanBase 的实践
  • Android tcp socket sample示例
  • 亚纳米级检测!潜望式棱镜的“检测密码”,决定手机远景清晰度
  • Text2SQL智能问答系统开发(一)
  • 激光雷达的单播和广播模式介绍
  • Java技术栈/面试题合集(17)-Git篇
  • C++符合快速入门(有java和js基础的)
  • 7.24路由协议总结
  • 如何将拥有的域名自定义链接到我的世界服务器(Minecraft服务器)
  • C++ 基础入门
  • 【shell脚本编程】day1 备份指定文件类型
  • 深入理解大语言模型生成参数:temperature、top\_k、top\_p 等全解析
  • 社区资源媒体管理系统设计与实现
  • 复盘—MySQL触发器实现监听数据表值的变化,对其他数据表做更新
  • Kubernetes Kubelet 资源配置优化指南:从命令行参数到配置文件的最佳实践
  • Hadoop磁盘I/O瓶颈的监控与优化:从iostat指标到JBOD vs RAID的深度解析