26

这也是我在 Miško Hevery 的一个关于依赖注入的谷歌谈话中的评论中提出的一个问题,但它被埋没在评论中。

我想知道将依赖项连接在一起的工厂/构建器步骤如何在 C++ 中工作。

即我们有一个依赖于 B 的类 A。构建器将在堆中分配 B,在 A 的构造函数中传递指向 B 的指针,同时在堆中分配并返回指向 A 的指针。

之后谁清理?建好后让builder清理好不好?这似乎是正确的方法,因为在谈话中它说构建器应该设置预期具有相同生命周期的对象,或者至少依赖项具有更长的生命周期(我对此也有疑问)。我在代码中的意思是:

class builder {
public:
    builder() :
        m_ClassA(NULL),m_ClassB(NULL) {
    }
    ~builder() {
        if (m_ClassB) {
            delete m_ClassB;
        }
        if (m_ClassA) {
            delete m_ClassA;
        }
    }
    ClassA *build() {
        m_ClassB = new class B;
        m_ClassA = new class A(m_ClassB);
        return m_ClassA;
    }
};

现在,如果有一个依赖项预计会比我们将其注入的对象的生命周期更长(比如 ClassC 就是那个依赖项),我知道我们应该将构建方法更改为:

ClassA *builder::build(ClassC *classC) {
    m_ClassB = new class B;
    m_ClassA = new class A(m_ClassB, classC);
    return m_ClassA;
}

您首选的方法是什么?

4

8 回答 8

13

这个演讲是关于 Java 和依赖注入的。

在 C++ 中,我们尝试传递 RAW 指针。这是因为 RAW 指针没有与之关联的所有权语义。如果您没有所有权,那么我们不知道谁负责清理该对象。

我发现大部分时间依赖注入是通过 C++ 中的引用完成的。
在您必须使用指针的极少数情况下,将它们包装在std::unique_ptr<>std::shared_ptr<>中,具体取决于您要如何管理所有权。
如果您不能使用 C++11 功能,请使用 std::auto_ptr<> 或 boost::shared_ptr<>。

我还要指出,C++ 和 Java 的编程风格现在如此不同,以至于将一种语言的风格应用于另一种语言将不可避免地导致灾难。

于 2008-12-09T14:47:20.957 回答
9

这很有趣,C++ 中的 DI 使用模板:

http://adam.younglogic.com/?p=146

我认为作者正在采取正确的行动,不要将 Java DI 翻译成 C++。值得一读。

于 2009-12-23T01:08:02.290 回答
6

我最近被 DI 虫子咬了。我认为它解决了很多复杂性问题,尤其是自动化部分。我已经编写了一个原型,它可以让你以一种漂亮的 C++ 方式使用 DI,或者至少我是这么认为的。您可以在这里查看代码示例:http: //codepad.org/GpOujZ79

显然缺少的东西:没有范围,没有接口与实现的绑定。后者很容易解决,前者我不知道。

如果这里有人对代码有意见,我将不胜感激。

于 2010-05-10T16:39:21.867 回答
3

使用 RAII。

将原始指针交给某人与将所有权交给他们相同。如果这不是你想要做的,你应该给他们某种也知道如何清理有问题的对象的外观。

shared_ptr<> 可以做到这一点;其构造函数的第二个参数可以是一个知道如何删除对象的函数对象。

于 2008-12-09T16:40:16.877 回答
2

如果您不一劳永逸地解决所有权问题,事情就会变得复杂。您只需在实现中决定依赖项是否可能比它们注入的对象寿命更长。

我个人会说不:注入依赖项的对象将在之后清理。尝试通过构建器来完成这意味着构建器必须比依赖项和注入它的对象寿命更长。在我看来,这导致的问题多于它解决的问题,因为在使用依赖注入的构建完成后,构建器不再提供任何有用的用途。

于 2008-12-09T14:33:02.983 回答
2

在 C++ 中,通常,当你做对了事情时,在大多数情况下你根本不需要编写析构函数。您应该使用智能指针自动删除内容。我认为,builder 看起来不像 ClassA 和 ClassB 实例的所有者。如果您不喜欢使用智能指针,您应该考虑对象的生命周期及其所有者。

于 2008-12-09T15:14:02.387 回答
2

根据我自己的经验,最好有明确的所有权规则。对于小的具体对象,最好使用直接复制,避免交叉依赖。

有时交叉依赖是不可避免的,并且没有明确的所有权。例如,(m) A 实例拥有 (n) B 实例,并且某些 B 实例可以由多个 As 拥有。在这种情况下,最好的方法是对 B 应用引用计数,方法类似于 COM 引用计数。任何拥有 B* 的函数必须首先增加引用计数,并在释放所有权时减少它。

我也避免使用 boost::shared_ptr 因为它创建了一个新类型(shared_ptr 和 B* 成为两种不同的类型)。我发现当我添加方法时它会带来更多的头痛。

于 2009-12-23T16:04:06.073 回答
1

您还可以检查FFEAD 依赖注入。它提供了类似于 Spring for JAVA 的 DI,并且具有一种不显眼的处理方式。它还具有许多其他重要功能,例如内置AJAX 支持、反射、序列化、C++ 解释器、C++ 业务组件、ORM、消息传递、Web 服务、线程池和支持所有这些功能的应用程序服务器。

于 2010-08-10T07:31:42.020 回答