C++——多态

📅 发布时间:2026/7/8 3:41:13 👁️ 浏览次数:
C++——多态
C 多态详解从基础到工程实践多态Polymorphism是面向对象编程的三大特性之一在 C 中主要通过虚函数virtual function实现是 C 中最强大也最容易出错的特性之一。下面从原理 → 用法 → 细节 → 常见陷阱 → 工程实践一次性把 C 多态讲透。1. 多态的核心概念C 中的多态指的是“同一个接口不同实现”通过基类指针或引用调用虚函数时实际执行的是派生类的版本而不是基类的版本。一句话总结“父类指针或引用指向子类对象时通过虚函数调用子类的实现”2. 多态的实现方式最核心的三种方式一虚函数 指针/引用最常用#includeiostreamusingnamespacestd;classAnimal{public:// 声明为虚函数virtualvoidspeak()const{coutAnimal is speaking...\n;}virtual~Animal()default;// 虚析构函数非常重要};classDog:publicAnimal{public:voidspeak()constoverride{// override 是 C11 推荐写法coutWoof! Woof!\n;}};classCat:publicAnimal{public:voidspeak()constoverride{coutMeow~\n;}};intmain(){Animal*animal1newDog();Animal*animal2newCat();animal1-speak();// 输出Woof! Woof!animal2-speak();// 输出Meow~deleteanimal1;deleteanimal2;return0;}关键点virtual只能在基类中首次声明子类可以不写virtual但强烈建议写override编译器会检查是否真的重写必须用指针或引用才能实现多态值传递会切片方式二虚函数表vtable原理面试常考C 通过虚函数表vtable实现多态。每个有虚函数的类都有一个隐藏的虚函数表指针vptrvptr 指向类的虚函数表vtable虚函数表是一个函数指针数组存储该类的所有虚函数地址子类会复制并修改父类的虚函数表覆盖对应位置内存布局示意Dog 对象Dog 对象内存 [ vptr ] → 指向 Dog 的 vtable [ 其他成员 ] Dog 的 vtable [ Dog::speak ] [ Animal::~Animal ] // 虚析构函数 ...3. 虚函数 vs 普通函数对比表特性普通成员函数虚函数 (virtual)是否多态否是调用方式静态绑定编译期确定动态绑定运行期通过 vptr开销几乎无一次虚表指针查找 间接调用是否可以被 override否是析构函数一般不虚必须虚否则内存泄漏风险4. 虚析构函数最重要、最常犯错的点永远记住只要一个类可能被当作基类使用就要把析构函数写成虚函数classBase{public:// 非虚析构 → 大坑// ~Base() { cout Base dtor\n; }virtual~Base(){coutBase dtor\n;}};classDerived:publicBase{public:~Derived(){coutDerived dtor\n;}};intmain(){Base*pnewDerived();deletep;// 如果 ~Base() 非虚只调用 Base 析构 → 内存泄漏// 如果是虚析构 → 先 Derived → 再 Base}结论基类必须有虚析构函数除非你明确知道这个类永远不会被 delete 通过基类指针虚析构函数会让类产生 vptr虚表指针增加 8 字节64 位系统5. 常见误区与陷阱用值传递实现不了多态切片问题voidmakeSound(Animal animal){// 值传递 → 切片animal.speak();// 永远调用 Animal::speak()}正确写法voidmakeSound(constAnimalanimal){// 引用或指针animal.speak();}override 与 finalC11 强烈推荐classBase{public:virtualvoidfunc()const0;// 纯虚函数};classDerived:publicBase{public:voidfunc()constoverridefinal{// 强制重写 禁止再被重写coutDerived func\n;}};不要在构造函数/析构函数中调用虚函数classBase{public:Base(){who();}// 危险virtualvoidwho(){coutBase\n;}};classDerived:publicBase{public:voidwho()override{coutDerived\n;}};→ 构造 Base 时Derived 还没构造完成who() 调用的是 Base 版本虚函数默认参数是静态绑定virtualvoidprint(intx10);→ 调用时用的是声明处的默认参数不是实际类型的6. 多态的工程实践建议2025–2026基类析构函数一律写 virtual除非你明确知道不会通过基类指针 delete尽量用 override 和 final编译期检查防止笔误优先用 const 引用传参const Animal接口类用纯虚函数0让子类必须实现避免在基类中放太多数据虚函数表 数据成员会增加对象体积需要运行时类型信息→ 用dynamic_cast有开销性能敏感场景→ 考虑 CRTP静态多态替代运行时多态7. 快速记忆口诀多态 virtual 指针/引用 运行时基类必须有虚析构子类重写用override禁止子类再重写用final不要值传递不要在构造/析构中调用虚函数接口类 →纯虚函数0想深入哪一部分比如虚函数表内存布局手撕多态 模板CRTP 静态多态dynamic_cast / typeid 用法纯虚函数 抽象类设计多重继承下的虚函数表告诉我具体想看哪一块我可以继续展开代码和图解。