绝大多数c++项目无需di框架,推荐用std::unique_ptr加工厂函数:构造清晰、所有权明确、零运行时开销、调试直观;依赖单向短生命周期时,仅需在main或测试中修改一行工厂函数即可替换mock。

不需要。绝大多数 C++ 项目完全没必要引入专门的 DI 框架,手写容器或第三方库反而容易带来编译变慢、调试困难、生命周期失控等实际问题。
什么时候 std::unique_ptr + 工厂函数就够了
这是最常见也最推荐的起点:构造逻辑清晰、所有权明确、无运行时开销、调试时单步就能看到对象怎么来的。
- 依赖是单向、短生命周期的(比如一个
Service持有唯一ILogger) - 测试时只需替换为
MockLogger,且只在main()或测试 fixture 中改一行工厂函数 - 没有跨模块共享实例的需求(即不需要“全局日志器”这种多处 resolve 同一对象的场景)
- 你不想为每个新类型都去注册、查文档、处理
resolve()失败异常
register_type 和 resolve 容易踩的坑
一旦开始用基于 std::unordered_map + std::type_index 的容器,以下问题会立刻浮现:
- 传入
container.register_instance<ilogger>(Logger())</ilogger>—— 绑定的是栈对象,后续访问就是未定义行为;必须用std::make_shared<consolelogger>()</consolelogger> -
resolve<ilogger>()</ilogger>返回std::shared_ptr<void></void>,不手动static_pointer_cast<ilogger></ilogger>就编译不过,且容易漏写 - 多个模块分别调用
register_type,谁先谁后?重复注册是否覆盖?没日志提示就静默失败 - 带参数构造(比如
Database(std::string host, int port))必须用 lambda 捕获,但值捕获可能悬挂,std::shared_ptr传参又增加间接层
用 std::function 封装构造逻辑比字符串注册更安全
别用 "ILogger" 这种字符串当 key —— 拼错就 resolve 失败,还无法被 IDE 跳转或编译器检查。用 typeid(T).name() 或 std::type_index(typeid(T)) 是底线。
-
std::type_index比字符串快,且类型名拼写错误会在编译期暴露(模板实例化失败) - 工厂函数签名统一为
std::function<:shared_ptr>()></:shared_ptr>,而不是std::function<:any></:any>—— 后者丢掉类型信息,resolve后还得自己转型 - 如果某依赖需要延迟初始化(比如首次
resolve才创建),工厂里加个static std::shared_ptr<t> instance</t>即可,不用整个框架支持“懒加载”特性
真正卡住人的从来不是“怎么注册”,而是“谁负责销毁”和“对象在哪块内存”。越早把所有权语义钉死(unique_ptr 表示独占,shared_ptr 表示共享),后期排查野指针和析构顺序就越省力。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











