虚函数(virtual)
虚函数virtual是基类指针/引用调用函数时程序在运行时自动识别对象真实类型执行子类重写的函数而不是固定调用基类函数。1.没有虚函数静态绑定早绑定编译器根据指针/引用的类型决定调用哪一个函数编译阶段就确定好了。class Bae{ public: void func(){ cout基类函数endl; } }; class Son:public Base{ public: void func(){ cout子类函数endl; } }; int main(){ Base*pnew Son() p-func();//没有virtual永远调用 Base::func() }明明指向了子类对象却执行了基类方法无法实现不同子类不同行为。2.加了virtual动态绑定晚绑定class Base{ public: virtual void func() { cout基类函数endl; } }; class Son:public Base{ public: void func() override { cout子类函数endl; } }; int main() { Base* pnew Son(); p-func();//运行时识别真实类型调用 Son::func() }override是 C11 引入的说明符专门用于子类重写虚函数。3.虚函数两大常见形态3.1 普通虚函数virtual void func();基类提供默认实现子类可选重写。3.2 纯虚函数virtual void SyncLog()0;基类没有函数实现拥有纯虚函数的类抽象类不能直接实例化对象子类必须重写该函数否则子类依然是抽象类无法创建对象 作用定义一套统一接口强制所有子类遵守规范。4.基类的析构函数加virtual的场景不是所有基类都要加virtual析构只有一种场景强制要求使用基类指针/引用指向子类对象并且会通过基类指针执行delete。我们把场景分成两大类区分4.1 ✅必须写virtual ~Base()的场景条件同时满足1.存在继承class Son:public Base2.业务代码会Base* pnew Son(); delete p;典型例子你当前的策略模式抽象基类LogStrategy所有面向多态设计、作为接口的抽象类含有纯虚函数这类类设计初衷就是让别人继承并用基类指针管理子类对象规范强制加上虚析构。class LogStrategy { public: virtual ~LogStrategy() default; // 必要 virtual void SyncLog(const std::string msg) 0; };4.2 ❌ 不需要、甚至不应该加 virtual 的场景场景 1不会通过基类指针 delete 子类虽然有继承但是永远不会执行delete 基类指针。 例如对象全部定义在栈上、智能指针使用子类类型、不执行销毁动作。Base obj; Son son; Base ref son; // 仅引用不delete不需要虚析构场景 2这个类禁止被继承C11finalclass Test final {}; // final类不能被继承完全不需要virtual析构场景 3只是普通工具类设计初衷就不允许别人继承很多底层工具类没有设计成多态基类继承只是偶然行为。C 编码规范如 Google 规范如果一个类不打算作为多态基类不要随便加 virtual。 代价产生虚表、增加对象内存开销。场景 4组合优先于继承根本不设计继承关系很多类不对外提供继承接口自然无需考虑。5.一条黄金判断口诀写代码直接套用如果你不确定未来会不会有人通过基类指针 delete 子类 → 保守加上虚析构如果你明确这个类不是用来做多态接口、禁止继承 → 不要加 virtual。6.一个高频误区澄清误区含有虚函数的类析构就必须 virtual错误 含有普通virtual函数 ≠ 一定会用基类指针delete子类。 只是绝大多数含有虚函数的类都是为了实现多态极大概率会执行delete 基类指针所以工程上约定俗成含有虚函数的基类一律写上 virtual 析构。