纯虚函数 vs 虚函数 区别(C++)
纯虚函数 vs 虚函数 核心区别一览特性虚函数纯虚函数声明方式virtual void func() {}virtual void func() 0;是否有实现✅ 必须有实现❌ 可以没有实现但也可以有子类是否必须重写❌ 可选✅必须重写除非子类也是抽象类类是否能实例化✅ 可以❌不能成为抽象类用途提供默认行为允许子类覆盖定义接口规范强制子类实现 代码示例cpp#include iostream using namespace std; // 基类 class Animal { public: // 1️⃣ 虚函数有默认实现子类可选覆盖 virtual void eat() { cout Animal: 吃东西 endl; } // 2️⃣ 纯虚函数没有实现子类必须覆盖 virtual void speak() 0; // 纯虚函数 // 3️⃣ 纯虚析构函数必须有实现 virtual ~Animal() 0; // 声明为纯虚 }; Animal::~Animal() { // ✅ 必须提供实现 cout Animal 析构 endl; } // 子类 class Dog : public Animal { public: // ✅ 必须实现纯虚函数 void speak() override { cout 汪汪! endl; } // ✅ 可选重写虚函数 void eat() override { cout 吃骨头 endl; } }; class Cat : public Animal { public: // ✅ 必须实现纯虚函数 void speak() override { cout 喵喵! endl; } // ❌ 不重写 eat()使用基类的默认实现 }; int main() { // ❌ Animal animal; // 错误Animal 是抽象类不能实例化 // ✅ 可以实例化子类 Dog dog; dog.eat(); // 吃骨头重写了基类 dog.speak(); // 汪汪! Cat cat; cat.eat(); // Animal: 吃东西使用基类默认 cat.speak(); // 喵喵! // 多态 Animal* animal new Dog(); animal-speak(); // 汪汪! delete animal; // Dog 析构 → Animal 析构 return 0; } 关键区别详解1️⃣ 虚函数提供可选覆盖的默认行为cppclass Skill { public: // 虚函数提供默认实现 virtual void onCast() { cout 释放技能默认特效 endl; } }; class FireBall : public Skill { public: // 可以重写也可以不重写 void onCast() override { cout 火球特效 endl; } }; class IceBolt : public Skill { // 不重写 → 使用基类的默认特效 };2️⃣ 纯虚函数强制必须实现的接口cppclass Skill { public: // 纯虚函数强制子类实现 virtual int getDamage() 0; }; class FireBall : public Skill { public: int getDamage() override { // ✅ 必须实现 return 100; } }; class IceBolt : public Skill { public: int getDamage() override { // ✅ 必须实现 return 80; } }; // ❌ 如果子类不实现编译错误 class NoDamageSkill : public Skill { // error: pure virtual function getDamage has no overrider }; 使用场景对比场景虚函数纯虚函数定义接口规范❌ 不够强制✅ 强制子类实现提供默认行为✅ 适合❌ 不适合基类可实例化✅ 可以❌ 不可以代码复用✅ 子类可继承实现❌ 子类必须自己实现设计模式模板方法模式策略模式、工厂模式 纯虚函数的特殊用法可以有实现cppclass Skill { public: // 声明为纯虚 virtual void execute() 0; }; // ✅ 但可以提供实现C 允许 void Skill::execute() { cout Skill: 默认执行逻辑 endl; } class FireBall : public Skill { public: void execute() override { // ✅ 可以调用基类的实现 Skill::execute(); // 先执行基类逻辑 cout 释放火球 endl; } }; class IceBolt : public Skill { public: void execute() override { Skill::execute(); // 也可以调用 cout ❄️ 释放冰霜 endl; } };应用场景基类提供通用逻辑子类在此基础上扩展。 游戏服务器中的实际应用示例 1技能系统cpp// 技能接口纯虚函数定义规范 class ISkill { public: virtual ~ISkill() {} virtual void execute() 0; // 纯虚必须实现 virtual int getDamage() 0; // 纯虚必须实现 virtual float getCooldown() 0; // 纯虚必须实现 }; // 基类提供通用功能虚函数 class BaseSkill : public ISkill { public: // 纯虚函数必须实现 void execute() override { // 通用执行逻辑 beforeExecute(); doExecute(); afterExecute(); } // 虚函数提供默认实现子类可覆盖 virtual void beforeExecute() { // 默认检查MP、CD等 } virtual void afterExecute() { // 默认记录日志 } // 纯虚函数子类必须实现 virtual void doExecute() 0; virtual int getDamage() 0; virtual float getCooldown() 0; }; // 具体技能 class FireBall : public BaseSkill { public: void doExecute() override { cout 火球爆炸 endl; } int getDamage() override { return 100; } float getCooldown() override { return 2.0f; } };示例 2怪物工厂cpp// 纯虚函数定义接口 class IMonsterFactory { public: virtual ~IMonsterFactory() {} virtual Monster* create() 0; // 纯虚必须实现 virtual string getType() 0; // 纯虚必须实现 }; // 具体工厂 class DragonFactory : public IMonsterFactory { public: Monster* create() override { return new Dragon(); } string getType() override { return Dragon; } }; 面试高频问题Q1: 什么是抽象类A: 含有至少一个纯虚函数的类叫做抽象类。抽象类不能实例化只能用作基类。Q2: 纯虚函数可以有实现吗A: 可以虽然不常见但 C 允许纯虚函数有实现。子类可以通过Base::func()调用基类的实现。Q3: 什么时候用虚函数什么时候用纯虚函数A:虚函数当基类能提供有意义的默认行为且允许子类选择是否覆盖时使用。纯虚函数当基类只是定义接口规范强制子类必须实现时使用。Q4: 析构函数可以是纯虚的吗A: 可以但必须提供实现因为析构子类对象时会调用基类析构函数。cppclass Base { public: virtual ~Base() 0; // 声明为纯虚 }; Base::~Base() { // ✅ 必须实现 cout Base 析构 endl; } 总结对比图text虚函数: ┌─────────────────────────────────┐ │ class Base { │ │ public: │ │ virtual void func() { │ ← 有默认实现 │ // 默认行为 │ │ } │ │ }; │ │ │ │ 子类可以重写也可以不重写 │ └─────────────────────────────────┘ 纯虚函数: ┌─────────────────────────────────┐ │ class Base { │ │ public: │ │ virtual void func() 0; │ ← 无实现0 │ }; │ │ │ │ 子类必须重写否则编译错误 │ │ Base 不能实例化抽象类 │ └─────────────────────────────────┘ 一句话总结虚函数 我给你一个默认实现你可以选择覆盖纯虚函数 我只定义接口你必须自己实现