🎯 纯虚函数 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️⃣ 虚函数:提供"可选覆盖"的默认行为
cpp
class Skill {
public:
// 虚函数:提供默认实现
virtual void onCast() {
cout << "释放技能(默认特效)" << endl;
}
};
class FireBall : public Skill {
public:
// 可以重写,也可以不重写
void onCast() override {
cout << "🔥 火球特效" << endl;
}
};
class IceBolt : public Skill {
// 不重写 → 使用基类的默认特效
};
2️⃣ 纯虚函数:强制"必须实现"的接口
cpp
class 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
};
📊 使用场景对比
| 场景 | 虚函数 | 纯虚函数 |
|---|---|---|
| 定义接口规范 | ❌ 不够强制 | ✅ 强制子类实现 |
| 提供默认行为 | ✅ 适合 | ❌ 不适合 |
| 基类可实例化 | ✅ 可以 | ❌ 不可以 |
| 代码复用 | ✅ 子类可继承实现 | ❌ 子类必须自己实现 |
| 设计模式 | 模板方法模式 | 策略模式、工厂模式 |
🔍 纯虚函数的特殊用法:可以有实现!
cpp
class 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: 可以,但必须提供实现!因为析构子类对象时会调用基类析构函数。
cpp
class 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 不能实例化(抽象类) │
└─────────────────────────────────┘
🎯 一句话总结
虚函数 = "我给你一个默认实现,你可以选择覆盖"
纯虚函数 = "我只定义接口,你必须自己实现"

668

被折叠的 条评论
为什么被折叠?



