c ++依赖注入:对象生命周期?

时间:2011-11-01 22:50:46

标签: c++ dependency-injection object-lifetime

我来自C#并试图将我的一些实践翻译成C ++。我使用原始指针在我的代码中的各个地方使用了依赖注入。然后我决定用std :: shared_ptr替换原始指针。作为该过程的一部分,有人建议我考虑使用堆栈分配的自动变量而不是动态分配它们(参见this question,尽管这个问题是在unique_ptr的上下文中,所以可能会有所不同)。

我相信以下示例显示了自动变量的使用。

class MyClass
{ 
public:
   MyClass(ApplicationService& app): appService_(app)
   {
   }

   ~MyClass()
   {
        appService_.Destroy(something);

   }
private:   
   ApplicationService& appService_;
}

class ConsumerClass
{
    DoSomething()
    {
        CustomApplicationService customAppService;
        MyClass myclass(customAppService);
        myclass...
    }
}

在上面的示例中,当customAppservice和myclass超出范围时,我如何知道哪个将首先销毁?如果首先销毁customAppService,那么MyClass析构函数将失败。这是在这种情况下使用shared_ptr的一个很好的理由还是有一个干净的方法?

更新

ApplicationService是一个类,它是与我的代码使用的第三方库交互所需的全局函数的包装器。我有这个课程,因为我认为它是支持单元测试和存根/模拟自由功能的标准方法。该类只是将调用委托给相应的全局函数。调用appService_.Destroy(something);实际上是在破坏MyClass的每个特定实例所使用的对象,而不是破坏Application类本身所做的任何操作。

2 个答案:

答案 0 :(得分:7)

答案是:无论如何,你不需要知道,因为你的设计已被打破。

首先,Destroy听起来像个坏主意,而且如果在一个不负责销毁另一个对象的对象中调用。来自Destroy方法的代码属于ApplicationService的析构函数(希望是虚拟的,尽管在这种情况下它实际上并不需要),这与C#完全相同。时间点。

一旦你完成了这个,你将(希望)意识到,MyClass摧毁appService_并不是它的责任,因为它不拥有它。它是ConsumerClass(或者更确切地说是DoSomething方法)的责任,它真正管理实际的服务,并且在您将Destroy的代码移入之后实际上会自动销毁它析构函数。 RAII如何以干净自动的方式实现一切,这不是很好吗?

class MyClass
{ 
public:
   MyClass(ApplicationService& app): appService_(app)
   {
   }

private:   
   ApplicationService& appService_;
}

class ConsumerClass
{
    DoSomething()
    {
        CustomApplicationService customAppService;
        MyClass myclass(customAppService);
        myclass...
    }
}

class ApplicationService
{
public:
    virtual ~ApplicationService()
    {
        //code from former Destroy method
    }
}

class CustomApplicationService
{
public:
    virtual ~CustomApplicationService()
    {
        //code from former Destroy method
    }
}

这是恕我直言,它是一个完美的干净C ++方式,问题绝对不是垃圾邮件shared_ptr的理由。即使你真的需要一个专用的Destroy方法并且无法将代码移动到析构函数中(我将其作为过度思考设计的动机),那么你仍然可以从{{1}调用Destroy再次, MyClass不负责销毁appService _

编辑:根据您的更新(以及我对DoSomething参数的愚蠢忽视),您的设计似乎确实非常正确(至少如果你不能改变{{1抱歉。

尽管类成员应该以相反的构造顺序销毁,但我不确定这也适用于本地自动变量。你可以做些什么来确保以定义的顺序调用析构函数是使用简单的块引入嵌套的作用域:

something

当然,完全没有必要使用动态分配,放弃ApplicationService。虽然嵌套的块稍微破坏了代码,但是没有理由以非动态的方式应用动态分配的丑陋而且没有理由,并且至少“以语义方式看起来很好看”void DoSomething() { CustomApplicationService customAppService; { MyClass myclass(customAppService); myclass... } // myclass destroyed } // customAppService destroyed 的声明在街区顶部;)

答案 1 :(得分:2)

在C ++中,对象通常会按照与创建它们的顺序完全相反的顺序销毁。

根据您的示例,MyClass将在CustomApplicationService

之前销毁On exit from a scope (however accomplished), destructors (12.4) are called for all constructed objects with automatic storage duration (3.7.2) (named objects or temporaries) that are declared in that scope, in the reverse order of their declaration. ... [Note: However, the program can be terminated (by calling exit() or abort()(18.3), for example) without destroying class objects with automatic storage duration. ]

例外情况是显式调用析构函数。但是,我认为你不应该在这个阶段关注这个例外。

另一个微妙之处叫做static initialization order fiasco。但是,这不适用于自动(堆栈)变量。

编辑:
从C ++ 2003开始 - 寻找'逆序'

6.6.0.2

{{1}}
相关问题