C++ 中带智能指针的依赖注入

Posted

技术标签:

【中文标题】C++ 中带智能指针的依赖注入【英文标题】:Dependency injection w/ smart pointers in C++ 【发布时间】:2017-09-11 22:44:10 【问题描述】:

我正在尝试在 C++ 项目中实现依赖注入模式,手动执行注入(无框架)。所以我使用工厂函数来手动创建依赖项并将它们传递给使用它们的类。我的问题是,当依赖关系图的根(本身是由智能指针管理的类实例)超出范围时,它的依赖关系(在类中引用的智能指针)不会被破坏(valgrind 显示丢失的内存块) .

我只是通过这样一个基本示例看到了这一点,其中 A 类依赖于 B 类:

class B
public:
    B() 
    ~B() 
;

class A

    std::unique_ptr<B> b_;

public:

    A(std::unique_ptr<B> b) : b_(std::move(b))  
    ~A()
;

namespace A_Factory 
    std::unique_ptr<A> getInstance()
        return std::make_unique<A>(std::make_unique<B>());
    


int main(void)
    auto a = A_Factory::getInstance();
    //do some A stuff
 //a goes out of scope and gets deleted?

所以在这个例子中我使用A_Factory::getInstance 来获取A,所以使用A 的东西不知道A 是如何创建的,A 也不知道它的依赖项是如何创建的。我的理解是,使用智能指针,它们会在超出范围时自动删除,希望一旦根超出范围,它会链接依赖关系图。如果我必须实现代码来销毁依赖项,那么我不会从智能指针中获得太多好处。

所以我不知道我是否没有正确设置智能指针,或者 valgrind 没有告诉我我认为这意味着什么。我对 valgrind 的理解是,它实际上只是指出了我的代码范围内的问题,所以如果标准库正在删除依赖项,那么 valgrind 可能不会反映这一点。然而,当我使用一个简单的类(无依赖关系)包装的智能指针对一个简单的程序运行 valgrind 时,它不会显示任何丢失的内存。

我还要注意,我在 *** 上看到的一些示例对我没有用,比如不在 A 的初始化列表中调用 std::move。而且我想避免传递任何原始指针。

【问题讨论】:

您是否尝试从 A 和 B 析构函数打印一些调试消息? 只是个书呆子,A(std::unique_ptr&lt;B&gt; b) 中的右值引用是否由于 Markdown 语法而丢失? &amp;amp; 有帮助吗? 【参考方案1】:

我正在使用不包含 make_unique 的 g++ 4.8.5,但我添加了 Herb Sutter 建议的 make_unique implementation 并在 valgrind 下运行测试,结果显示没有泄漏。

#include <iostream>
#include <cstdlib>
#include <memory>

using namespace std;

template<typename T, typename ...Args>
std::unique_ptr<T> make_unique(Args&& ...args)

   return std::unique_ptr<T>(new T ( std::forward<Args>(args)...));


class B
public:
    B() 
    ~B() 
;

class A

    std::unique_ptr<B> b_;

public:

    A(std::unique_ptr<B> b) : b_(std::move(b))  
    ~A()
;

namespace A_Factory 
    std::unique_ptr<A> getInstance()
        return make_unique<A>(make_unique<B>());
    


int main(void)
   auto a = A_Factory::getInstance();

也许您的问题源于您使用的任何编译器中的 make_unique 实现?

【讨论】:

由于遵循霍华德的回答,我发现了这个问题,但是我确实发现了 valgrind 报告的内存丢失的显着差异,具体取决于实现。修复问题后,g++ 5.4.0 (Ubuntu) 没有显示任何问题,但 LLVM 8.1.0 (mac OS Sierra) 仍然显示大量丢失记录。【参考方案2】:

我已经在 all 构造函数和析构函数中使用 print 语句对您的代码进行了检测。此外,我禁用了所有编译器生成的构造函数以确保它们不会运行:

#include <iostream>
#include <memory>

class B
public:
    B() std::cout << "B()\n";
    ~B() std::cout << "~B()\n";
    B(const B&) = delete;
;

class A

    std::unique_ptr<B> b_;

public:

    A(std::unique_ptr<B> b) : b_(std::move(b)) std::cout << "A()\n";
    ~A()std::cout << "~A()\n";
    A(const A&) = delete;
;

namespace A_Factory 
    std::unique_ptr<A> getInstance()
        return std::make_unique<A>(std::make_unique<B>());
    


int main(void)
    auto a = A_Factory::getInstance();
    //do some A stuff

这个程序为我输出:

B()
A()
~A()
~B()

我为每个析构函数计算一个构造函数。你对你的编译器有同样的看法吗?如果是这样,一切都很好。

【讨论】:

这让我找到了真正的罪魁祸首。我提供的示例代码并没有准确地反映我正在测试的代码,但有一个关键区别:每个类都是纯虚拟抽象类的实现,并且我没有在抽象类中声明虚拟析构函数。因此,在这样做之后,调用了析构函数,并且 valgrind 不再报告丢失的内存块。 很高兴我能帮上忙。

以上是关于C++ 中带智能指针的依赖注入的主要内容,如果未能解决你的问题,请参考以下文章

C++|深入理解智能指针

C++智能指针

C++智能指针详解:智能指针的引入

C++ 和智能指针——智能指针在这种情况下有啥帮助?

C++编程经验:智能指针 -- 裸指针管得了的我要管,裸指针管不了的我更要管!

C++编程经验:智能指针 -- 裸指针管得了的我要管,裸指针管不了的我更要管!