【问题标题】:[Boost]::DI creating unique shared_ptr objects from injector[Boost]::DI 从注入器创建唯一的 shared_ptr 对象
【发布时间】:2018-03-31 16:43:35
【问题描述】:

使用这个示例代码,我希望得到这样的结果 button1 和 button2 是两个独立的对象。

#include <iostream>
#include <memory>
#include "di.hpp"
namespace di = boost::di;

struct CommandQueue {
    void addCommand() {}
};

struct Control {
    Control( CommandQueue &cq ) : cq( cq ) {
        static int sid{};
        id = ++sid;
    }

    CommandQueue& cq;
    int id{};
};

int main() {

    auto injector = di::make_injector( di::bind<CommandQueue>().in(di::singleton) );

    auto button1 = injector.create<std::shared_ptr<Control>>();
    auto button2 = injector.create<std::shared_ptr<Control>>();

    std::cout << "button1->id = " << button1->id << std::endl;
    std::cout << "button2->id = " << button2->id << std::endl;

    return 0;
}

当前输出为:

button1->id = 1

button2->id = 1

而不是预期的:

button1->id = 1

button2->id = 2

CommandQueue 单例中删除 di::singleton 生命周期范围也不能修复它。 我知道默认情况下 shared_ptr 的生命周期范围是单例,但我认为这是指注入的依赖项,而不是使用 create 创建的实际对象。

【问题讨论】:

  • addCommand的相关性是什么
  • 这只是一个占位符来证明使用 CommandQueue 作为单例是合理的,但即使没有这个,代码仍然会创建 1 个对象而不是 2 个

标签: c++ boost dependency-injection shared-ptr


【解决方案1】:

其实最简单的可能是

auto button1 = injector.create<Control>();
auto button2 = injector.create<Control>();

std::cout << "button1.id = " << button1.id() << std::endl;
std::cout << "button2.id = " << button2.id() << std::endl;

打印

button1.id = 1
button2.id = 2

如果您必须拥有共享指针,那么接下来最简单的事情就是

auto button1 = std::make_shared<Control>(injector.create<Control>());
auto button2 = std::make_shared<Control>(injector.create<Control>());

std::cout << "button1->id = " << button1->id() << std::endl;
std::cout << "button2->id = " << button2->id() << std::endl;

概念上你想要一个控制工厂,而不是一个控件。所以,你应该考虑从依赖容器创建一个工厂:

#include <boost/di.hpp>
#include <iostream>
#include <memory>
namespace di = boost::di;

struct CommandQueue {
    void addCommand() {}
};

struct Control {
    Control(CommandQueue &cq) : _cq(cq), _id(idgen()) { }

    int id() const { return _id; }

    struct Factory {
        Factory(CommandQueue& cq) : _cq(cq) {}
        CommandQueue& _cq;

        template <typename... Args>
        auto operator()(Args&&... args) const {
            return std::make_shared<Control>(_cq, std::forward<Args>(args)...);
        }
    };
  private:
    static int idgen() { static int sid{}; return ++sid; }
    CommandQueue &_cq;
    int _id{};
};

int main() {
    auto injector = di::make_injector(di::bind<CommandQueue>().in(di::singleton));

    auto factory = injector.create<Control::Factory>();
    auto button1 = factory();
    auto button2 = factory();

    std::cout << "button1->id = " << button1->id() << std::endl;
    std::cout << "button2->id = " << button2->id() << std::endl;
}

【讨论】:

  • 谢谢,我不想破坏我们库中的 OCP,因此将 shared_ptr 转换为对象不是一个好选择,我已经考虑过您在您的建议中建议的“裸”make_shared第二个例子,但我不确定这是使用 DI 的最佳实践,最后是的,我已经将真实代码封装到工厂中,但我仍然想知道使用 DI 是否可以避免在左侧调用 make_shared创建,我不想这样做以避免深度复制并且必须编写复制ctor。基本上我只是想再次保证我没有做任何非常错误的事情:)
  • 你是不是很详细地告诉我,我浪费时间下载 boost DI、阅读文档(和代码,一闪而过)并提出建议?
  • 嗯不,你为什么会这样想?我想详细说明我的思考过程,但我不确定是否要编辑带有额外信息的原始问题或作为评论回复
  • 好吧。我不确定要在我的答案中添加什么。通常的做法是,如果有帮助就投赞成票,如果没有帮助就投反对票。在评论中重申这个前提并不是很有成效。放心
  • 非常感谢您花时间详细说明您的答案,赞成,我只是在等着看是否还有其他答案,然后再将其打成绿色。
猜你喜欢
  • 2022-01-16
  • 2012-07-21
  • 1970-01-01
  • 1970-01-01
  • 2023-03-07
  • 1970-01-01
  • 2023-04-04
  • 2018-04-14
  • 2023-03-05
相关资源
最近更新 更多