在C++中,虚函数是实现多态的传统方式,但并非唯一选择。过度依赖虚函数可能导致派生类与基类的强耦合,或难以在运行时灵活切换行为。《Effective C++》Item35指出:应根据场景选择更合适的替代方案,包括NVI模式、函数指针、策略模式等。本文解析这些方案的原理、适用场景及实践方式。
一、虚函数的局限性
虚函数的核心是“基类定义接口,派生类提供实现”,但存在以下短板:
- 行为扩展受限:派生类只能通过重写虚函数修改行为,难以在调用前后添加统一逻辑(如日志、权限检查)。
- 运行时切换困难:虚函数的行为绑定在派生类类型上,无法动态替换单个对象的行为(需创建新对象)。
- 耦合性高:派生类必须继承基类,无法复用非继承关系的实现。
示例:虚函数难以添加统一前置逻辑
class GameCharacter {
public:// 虚函数:派生类各自实现攻击逻辑virtual void attack() const = 0;
};class Warrior : public GameCharacter {
public:void attack() const override {std::cout << "Warrior swings sword" << std::endl;}
};class Mage : public GameCharacter {
public:void attack() const override {std::cout << "Mage casts fireball" << std::endl;}
};
若需为所有attack
添加“消耗体力”的前置逻辑,需修改每个派生类的实现,违反开闭原则。
二、替代方案及实践
1. NVI(Non-Virtual Interface)模式:用非虚函数包裹虚函数
原理:基类提供public非虚函数作为接口,在其中调用protected虚函数(派生类仅需重写虚函数)。非虚函数可添加统一逻辑(如日志、前置检查)。
class GameCharacter {
public:// 非虚接口:固定流程,不可重写void attack() const {// 统一前置逻辑:消耗体力consumeStamina(