纯虚函数 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️⃣ 虚函数:提供"可选覆盖"的默认行为

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 不能实例化(抽象类)       │
└─────────────────────────────────┘

🎯 一句话总结

虚函数 = "我给你一个默认实现,你可以选择覆盖"
纯虚函数 = "我只定义接口,你必须自己实现"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值