当前位置: 首页 > news >正文

Effective C++ 条款35:考虑 virtual函数以外的其他选择

Effective C++ 条款35:考虑virtual函数以外的其他选择


核心思想虚函数并非多态行为的唯一实现方式。通过函数指针、std::function、策略模式等替代方案,可以降低耦合度、提高灵活性、增强可测试性,同时避免虚函数带来的性能开销和二进制兼容性问题。

⚠️ 1. 虚函数的局限性

主要问题

问题类型具体表现影响
编译依赖虚函数修改导致所有派生类重新编译大型系统编译时间显著增加
性能开销虚函数调用(vptr查找+间接跳转)高频调用场景性能瓶颈
扩展不灵活无法在运行时动态改变行为系统重启才能切换算法
二进制兼容性添加虚函数破坏库的ABI兼容性强制客户端重新编译部署
多继承复杂性菱形继承导致虚函数表结构复杂调试困难,性能额外开销

性能对比

// 测试环境:x86-64, GCC 12.2
const int N = 1'000'000'000;// 虚函数调用
virtual void vfunc() { ++count; }  // 耗时: 3.8 ns/call// 普通函数调用
void func() { ++count; }           // 耗时: 0.9 ns/call// 函数指针调用
void (*pfunc)() = &func;           // 耗时: 1.2 ns/call

🚨 2. 核心替代方案

方案1:函数指针(C风格)

class GameCharacter;
using HealthCalcFunc = int (*)(const GameCharacter&);class GameCharacter {
public:explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc): healthFunc(hcf) {}int healthValue() const { return healthFunc(*this); }
private:HealthCalcFunc healthFunc;
};// 自定义计算函数
int customHealthCalc(const GameCharacter& gc) {// 定制化逻辑
}
GameCharacter wizard(customHealthCalc);

方案2:std::function(现代C++)

using HealthCalculator = std::function<int (const GameCharacter&)>;class GameCharacter {
public:explicit GameCharacter(HealthCalculator hc = defaultHealthCalc): healthCalc(std::move(hc)) {}int healthValue() const {return healthCalc(*this);}
private:HealthCalculator healthCalc;
};// 使用示例:
// 1. 函数对象
struct CustomCalculator {int operator()(const GameCharacter&) const { ... }
};
GameCharacter warrior(CustomCalculator{});// 2. Lambda表达式
GameCharacter elf([](const GameCharacter& gc) {return gc.level() * 10;
});// 3. 绑定成员函数
class GameSystem {
public:int complexCalc(const GameCharacter&) const;
};
GameSystem system;
GameCharacter boss(std::bind(&GameSystem::complexCalc, &system, _1));

方案3:策略模式(面向对象)

class HealthCalcStrategy {
public:virtual ~HealthCalcStrategy() = default;virtual int calculate(const GameCharacter&) const = 0;
};class DefaultHealthStrategy : public HealthCalcStrategy { ... };
class PoisonHealthStrategy : public HealthCalcStrategy { ... };class GameCharacter {
public:explicit GameCharacter(std::unique_ptr<HealthCalcStrategy> hcs): healthStrategy(std::move(hcs)) {}int healthValue() const {return healthStrategy->calculate(*this);}
private:std::unique_ptr<HealthCalcStrategy> healthStrategy;
};// 运行时切换策略
auto character = GameCharacter(std::make_unique<DefaultHealthStrategy>());
character.setStrategy(std::make_unique<PoisonHealthStrategy>()); // 添加setter

⚖️ 3. 方案选型指南
场景推荐方案优势局限性
C兼容接口🔶 函数指针零开销,兼容C代码类型不安全,功能有限
灵活回调机制std::function支持多种可调用对象,类型安全轻微性能开销
复杂算法族✅ 策略模式易扩展,符合开闭原则对象生命周期管理复杂
编译时多态✅ 模板策略零运行时开销,强类型检查代码膨胀,接口约束弱
极高性能要求🔶 CRTP模式静态多态无虚函数开销语法复杂,灵活性降低

现代C++增强

// 编译时策略选择(模板)
template<typename HealthPolicy = DefaultHealthPolicy>
class GameCharacter {
public:int healthValue() const {return HealthPolicy::calculate(*this);}
};// 使用
class CustomPolicy {
public:static int calculate(const GameCharacter&) { ... }
};
GameCharacter<CustomPolicy> hero;// C++20概念约束
template<typename T>
concept HealthPolicy = requires(T p, const GameCharacter& gc) {{ p.calculate(gc) } -> std::convertible_to<int>;
};template<HealthPolicy HP = DefaultHealthPolicy>
class GameCharacter { ... };

💡 关键设计原则

  1. 静态多态(CRTP)

    template<typename Derived>
    class HealthCalculator {
    public:int calculate() const {return static_cast<const Derived&>(*this).implCalculate();}
    };class Mage : public HealthCalculator<Mage> {
    public:int implCalculate() const {return mana_ * 2 + level_ * 5;}
    };// 使用
    Mage merlin;
    auto health = merlin.calculate(); // 静态绑定,无虚函数开销
    
  2. 策略工厂模式

    class HealthStrategyFactory {
    public:using StrategyMap = std::unordered_map<std::string, std::function<std::unique_ptr<HealthCalcStrategy>()>>;static std::unique_ptr<HealthCalcStrategy> create(const std::string& name) {auto it = strategies().find(name);return it != strategies().end() ? it->second() : nullptr;}static void registerStrategy(const std::string& name, auto creator) {strategies()[name] = creator;}private:static StrategyMap& strategies() {static StrategyMap instance;return instance;}
    };// 注册策略
    HealthStrategyFactory::registerStrategy("Poison", []{ return std::make_unique<PoisonHealthStrategy>(); });// 运行时创建
    auto strategy = HealthStrategyFactory::create("Poison");
    
  3. 性能敏感场景优化

    // 数据导向设计(Data-Oriented Design)
    class GameCharacterSystem {
    public:void updateHealth() {for (auto& [id, charData] : characters_) {charData.health = healthFuncs_[charData.type](charData);}}using HealthFunc = int (*)(const CharacterData&);std::vector<HealthFunc> healthFuncs_; // 函数指针表std::unordered_map<int, CharacterData> characters_;
    };
    

典型虚函数场景

class GameObject {
public:virtual void update() = 0; // 每帧更新virtual void render() const = 0; // 渲染
};// 问题:添加新游戏对象需继承,修改基类影响所有派生类

重构为策略模式

class UpdateStrategy {
public:virtual void update(GameObject&) = 0;
};class RenderStrategy {
public:virtual void render(const GameObject&) = 0;
};class GameObject {
public:void update() { updateStrategy_->update(*this); }void render() const { renderStrategy_->render(*this); }void setUpdateStrategy(std::unique_ptr<UpdateStrategy> us) {updateStrategy_ = std::move(us);}// 类似设置渲染策略...
private:std::unique_ptr<UpdateStrategy> updateStrategy_;std::unique_ptr<RenderStrategy> renderStrategy_;
};// 运行时切换渲染策略
character.setRenderStrategy(std::make_unique<VulkanRenderStrategy>());

函数式编程示例

using DamageCalculator = std::function<int (const GameCharacter& attacker, const GameCharacter& target)>;class CombatSystem {
public:void setDamageCalculator(DamageCalculator dc) {damageCalc_ = std::move(dc);}int calculateDamage(/*...*/) {return damageCalc_(attacker, target);}
private:DamageCalculator damageCalc_;
};// 配置不同伤害算法
combat.setDamageCalculator([](auto& a, auto& t) {return a.strength() * 10 - t.armor(); // 物理伤害
});combat.setDamageCalculator([](auto& a, auto& t) {return a.intelligence() * 5; // 魔法伤害
});
http://www.dtcms.com/a/326172.html

相关文章:

  • Spring源码解析 - SpringApplication run流程-prepareContext源码分析
  • MD5:理解MD5 / MD5核心特性 / MD5 在前端开发中的常见用途 / 在线生成MD5 / js-md5
  • Linux Docker 运行SQL Server
  • loading效果实现原理
  • Elasticsearch Node.js 客户端的安装
  • 第六十一章:AI 模型的“视频加速术”:Wan视频扩散模型优化
  • 简单清晰的讲解一下RNN神经网络
  • HarmonyOS 开发入门 第一章
  • 力扣面试150题--阶乘后的零,Pow(x,n)直线上最多的点
  • Win10电脑密码忘记如何进入操作系统
  • 基于AS32S601芯片的商业卫星光纤放大器(EDFA)抗单粒子效应解决方案研究
  • 前端组件库双雄对决:Bootstrap vs Element UI 完全指南
  • Speech Databases of Typical Children and Children with SLI 数据集解读
  • 飞轮储能和超级电容综合对比分析
  • LintCode第1181题-二叉树的直径
  • VBA即用型代码手册:计算选择的单词数Count Words in Selection
  • (Arxiv-2025)Phantom-Data:迈向通用的主体一致性视频生成数据集
  • MathType关联Wps实现公式编辑【Tex语法适配】
  • 使用行为树控制机器人(一) —— 节点
  • 【C++语法】输出的设置 iomanip 与 std::ios 中的流操纵符
  • 金蝶云星辰模拟报价功能助力企业快速决策
  • CV 医学影像分类、分割、目标检测,之分类项目拆解
  • Nginx(企业高性能web服务器)
  • 需求优先级如何划分
  • AI炼丹日志-32- memvid 大模型数据库!用视频存储+语义检索实现秒级搜索
  • Pluto Pillow如何靠 “私人定制” 枕头引爆海外市场
  • 学习笔记|decorator 装饰器是什么?
  • 2025-8-11-C++ 学习 暴力枚举(2)
  • 【Linux文件操作】文件操作系统调用
  • [激光原理与应用-231]:光学 - 光学的主要分支、研究对象、应用场合与职业方向(几何光学、物理光学、量子光学、集成光学、非线性光学制造工艺、光学系统设计)