9

我来自 C#,并试图将我的一些实践翻译成 C++。我使用原始指针在整个代码中的各个地方使用了依赖注入。然后我决定用 std::shared_ptr 替换原始指针。作为该过程的一部分,建议我考虑使用堆栈分配的自动变量而不是动态分配它们(尽管该问题是在 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 是一个类,它是与我的代码使用的第 3 方库交互所需的全局函数的包装器。我有这门课,因为我相信这是支持单元测试和独立功能的存根/模拟的标准方法。此类只是将调用委托给相应的全局函数。调用 appService_.Destroy(something); 实际上是在破坏 MyClass 的每个特定实例使用的对象,而不是破坏与 Application 类本身有关的任何事情。

4

2 回答 2

7

答案是:你不需要知道,因为你的设计已经被破坏了。

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

完成此操作后,您将(希望)意识到,MyClass销毁不是 的责任appService_,因为它不拥有它。这是ConsumerClass(或者更确切地说是DoSomething方法)的责任,它真正管理实际的服务,并且一旦你将Destroy's 的代码移动到析构函数中,它实际上会自动销毁它。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方法并且无法将代码移动到析构函数中(我认为这是过度思考设计的动机),那么您仍然会再次调用,DestroyMyClass不负责销毁 appService_DoSomething

编辑:根据您的更新(以及我对something参数的愚蠢忽略),您的设计似乎确实非常正确(至少如果您不能更改ApplicationService),对不起。

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

void DoSomething()
{
    CustomApplicationService customAppService;
    {
        MyClass myclass(customAppService);
        myclass...
    }       // myclass destroyed
}       // customAppService destroyed

当然还是完全没有必要使用动态分配shared_ptr的,暂且不说。尽管嵌套的块有点破坏代码,但它与以非动态方式且没有理由地应用的动态分配的丑陋无关,并且它至少“在语义上看起来不错”customAppService在块的顶部声明;)

于 2011-11-01T23:14:26.907 回答
2

在 C++ 中,一般来说,对象的销毁顺序与创建它们的顺序完全相反。

根据您的示例,MyClass将在之前销毁CustomApplicationService

显式调用析构函数时例外。但是,我认为您在这个阶段不应该关心这个例外。

另一个微妙之处被称为静态初始化顺序惨败。但是,这不适用于自动(堆栈)变量。

编辑:
来自 C++2003 - 寻找“逆序”

6.6.0.2

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. ]
于 2011-11-01T22:54:14.907 回答