全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-690-7320

c++如何实现一个简单的依赖注入(DI)容器_c++控制反转(IoC)设计【架构】

C++轻量级DI容器通过控制反转将对象创建与依赖管理交由外部容器处理,基于C++17+实现,强调接口与实现分离、依赖抽象而非具体。

c++如何实现一个简单的依赖注入(di)容器_c++控制反转(ioc)设计【架构】

在 C++ 中实现一个轻量级依赖注入(DI)容器,核心是把对象的创建和依赖关系的管理从类内部抽离出来,交由外部容器统一控制——这就是控制反转(IoC)的本质。它不依赖框架,用标准 C++17+ 就能写出清晰、可测试、松耦合的结构。

一、明确接口与实现分离(面向抽象编程)

DI 的前提是“依赖于抽象,而非具体”。先定义接口(纯虚类),再提供具体实现:

struct ILogger {
    virtual ~ILogger() = default;
    virtual void log(const std::string& msg) = 0;
};

struct ConsoleLogger : ILogger {
    void log(const std::string& msg) override {
        std::cout << "[LOG] " << msg << "\n";
    }
};

struct FileLogger : ILogger {
    std::string filename;
    FileLogger(const std::string& f) : filename(f) {}
    void log(const std::string& msg) override {
        std::ofstream out(filename, std::ios::app);
        out << "[FILE] " << msg << "\n";
    }
};

二、设计一个简易 DI 容器(支持单例/瞬态/工厂注册)

容器本质是一个类型到创建逻辑的映射表。用 std::unordered_map + std::function + std::any(或类型擦除)即可支撑多态注册。以下是最小可行版本(无线程安全,适合学习):

class DIContainer {
private:
    std::unordered_map<std::type_index, std::function<std::any()> > creators;
    std::unordered_map<std::type_index, std::any> instances; // 单例缓存

public:
    template<typename T, typename Impl = T>
    void register_type() {
        creators[std::type_index(typeid(T))] = []() -> std::any {
            return std::make_unique<Impl>();
        };
    }

    template<typename T>
    void register_instance(std::unique_ptr<T> instance) {
        instances[std::type_index(typeid(T))] = std::move(instance);
        creators.erase(std::type_index(typeid(T))); // 优先走实例
    }

    template<typename T>
    std::unique_ptr<T> resolve() {
        auto type_idx = std::type_index(typeid(T));
        if (instances.find(type_idx) != instances.end()) {
            return std::any_cast<std::unique_ptr<T>>(instances[type_idx]);
        }
        if (creators.find(type_idx) != creators.end()) {
            auto obj = creators[type_idx]();
            return std::any_cast<std::unique_ptr<T>>(obj);
        }
        throw std::runtime_error("Type not registered: " + std::string(typeid(T).name()));
    }
};

三、支持构造函数注入(手动传递依赖)

C++ 没有反射,无法自动解析构造参数,所以需显式注册带依赖的工厂函数。这是最实用、最可控的方式:

Procys Procys

AI驱动的发票数据处理

Procys 102 查看详情 Procys
  • 让容器知道 “如何用 ILogger 构建 Service”
  • 注册时捕获已解析的依赖(如 logger),绑定为 lambda
  • 避免模板爆炸,也利于单元测试替换 mock
// 假设服务依赖 ILogger
struct Service {
    std::unique_ptr<ILogger> logger;
    Service(std::unique_ptr<ILogger> l) : logger(std::move(l)) {}
    void do_work() { logger->log("working..."); }
};

// 注册方式:显式声明依赖
container.register_type<ILogger, ConsoleLogger>(); // 先注册 ILogger 实现

container.creators[std::type_index(typeid(Service))] = [&container]() -> std::any {
    auto logger = container.resolve<ILogger>();
    return std::make_unique<Service>(std::move(logger));
};

四、使用示例:组合即配置

主程序不再 new 对象,只跟容器打交道;切换实现只需改注册,不碰业务代码:

int main() {
    DIContainer container;

    // 方式1:绑定具体实现(ConsoleLogger)
    container.register_type<ILogger, ConsoleLogger>();

    // 方式2:或换为文件日志(一行切换)
    // container.register_instance(std::make_unique<FileLogger>("app.log"));

    // 注册 Service(含依赖解析逻辑)
    container.creators[std::type_index(typeid(Service))] = [&container]() -> std::any {
        auto logger = container.resolve<ILogger>();
        return std::make_unique<Service>(std::move(logger));
    };

    // 使用
    auto svc = container.resolve<Service>();
    svc->do_work(); // 输出 [LOG] working...

    return 0;
}

基本上就这些。不需要宏、不依赖第三方,靠类型擦除 + 函数对象 + RAII 就能落地。关键不是“全自动”,而是“谁负责创建、谁负责连接”变得清晰可管理。真实项目中可在此基础上加作用域(transient/scoped/singleton)、生命周期钩子、模块化注册(Module 类)、或对接 Boost.DI / Dice 等成熟库。但原理始终一致:把 new 和 new 的顺序,交给容器说了算。

以上就是c++++如何实现一个简单的依赖注入(DI)容器_c++控制反转(IoC)设计【架构】的详细内容,更多请关注其它相关文章!


# 前向  # 宿州关键词排名推广  # 三水短视频关键词排名  # 抖音搜索seo脑图  # 整体推广营销方案  # 苹果线下营销推广方案  # 电扇品牌营销推广  # 酒店网站建设开发流程  # 塔城seo排名  # 抖音营销推广文案图片  # 佛山搜狗seo哪家专业  # c++  # 游戏开发  # 如何处理  # 尼克  # 多态  # 而非  # 就能  # 如何用  # 递归  # 如何实现  # 依赖注入 


相关文章: PHP表单提交后函数重复执行的解决方案:管理$_POST数据  Golang如何实现Web文件静态资源服务器_Golang静态资源服务器开发与实践  在React函数组件中利用原生HTML5进行邮箱地址验证  必由学官网首页入口 必由学教师网页版登录指南  MAC如何安全彻底地删除文件_MAC使用终端命令确保文件无法被恢复  C++如何比较两个字符串_C++ string compare函数与操作符对比  PHP字符串中复杂变量插值的最佳实践与语法解析  Yandex官网搜索引擎免登录_俄罗斯Yandex一键直达入口  解决Tabulator日期时间排序问题的专业指南  J*aScript中localStorage数据的获取、清洗与格式化教程  Pandas DataFrame:高效添加条件计算列  将JSON对象数组转置为键值对列表的实用指南  Eclipse怎么运行工程_Eclipse工程运行配置说明  汽水音乐在线版入口_汽水音乐网页播放手册  J*aScript实现动态背景色下的文本与按钮颜色自适应调整  J*a如何实现并发下载文件_J*a多线程IO性能优化案例  jQuery Mask 插件中实现电话号码固定前导零的教程  在Qt QML中通过Python字典动态更新TextEdit内容的教程  QQ邮箱登录首页官网地址2026 QQ邮箱官方网页入口  Go与Ruby之间实现AES加密互通:CFB模式下的密钥长度匹配策略  iwriter统一登录平台 iwrite账号密码登录页面  PHP中高效并行检查多链接状态的教程  Animex动漫社网入口地址 Animex动漫社网正版在线入口  b站怎么删除评论_b站评论管理与删除操作  2025AO3夸克浏览器通道_AO3手机HTTPS安全入口分享  Go语言中对Map值调用带指针接收者方法:原理与最佳实践  处理动态列数据:J*a ArrayList的正确初始化与字符累加教程  Windows7怎么硬盘安装 Windows7提取ISO镜像到非系统盘并运行setup.exe实现硬盘直装【教程】  Golang如何使用context实现超时取消_Golang context超时取消模式实践  CSS Grid如何控制元素对齐_align-items与justify-items组合使用  Golang如何通过reflect获取匿名字段方法_Golang reflect匿名字段方法访问技巧  天猫双十一预售商品怎么退款_天猫双十一预售退款操作指南  文心一言怎样用批量生成做多版文案_文心一言用批量生成做多版文案【批量创作】  处理嵌套交互式控件:前端可访问性指南  Angular Material 垂直步进器:实现底部到顶部排序的教程  Win11 BitLocker密码忘了怎么办 Win11找回BitLocker恢复密钥方法【解决】  我的世界官方游戏入口 我的世界官网平台直达链接  随机参数递归函数的基准调用次数与时间复杂度探究  Win11怎么查看显卡显存 Win11显示适配器属性及专用视频内存查询  聚水潭ERP登录页面入口 聚水潭ERP官网登录界面  大麦的“候补”是什么意思 大麦候补购票规则【详解】  J*a TimerTask文件监控:HashMap状态管理与常见陷阱规避指南  在J*a项目里如何构建对象之间的契约_接口约束的实际落地  向日葵客户端怎么进行远程CentOS控制_向日葵客户端远程CentOS控制操作教程  单12V-2&#215;6实现为RTX 5090供电750W!甚至都没敢跑分  Log4j Console Appender性能瓶颈与高并发优化策略  抖音DOU+怎么投最有效 抖音付费推广的ROI提升技巧  Lar*el Migration:重命名列后添加新列的正确操作顺序  Lar*el递归关系中排除子孙节点的策略  QQ邮箱登录平台入口 QQ邮箱网页版邮箱官方入口 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。