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

发布于 2024-12-13 10:36:22 字数 1107 浏览 4 评论 0原文

我来自 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 是一个类,它是与我的代码使用的第三方库交互所需的全局函数的包装器。我有这个类,因为我相信它是支持单元测试和独立函数的存根/模拟的标准方法。该类只是将调用委托给相应的全局函数。调用 appService_.Destroy(something);实际上是销毁 MyClass 的每个特定实例使用的对象,而不是销毁与 Application 类本身有关的任何内容。

I'm coming from C# and trying to translate some of my practices into C++. I've used dependency injection in various places throughout my code using raw pointers. Then I decide to replace the raw pointers with std::shared_ptr's. As part of that process it was suggested that I consider using stack allocated automatic variables rather than dynamically allocating them (see this question although that question was in the context of unique_ptr so maybe that is different).

I believe the below example shows the use of automatic variables.

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

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

   }
private:   
   ApplicationService& appService_;
}

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

In the above example, when customAppservice and myclass go out of scope how do I know which will be destroyed first? If customAppService is destroyed first than the MyClass destructor will fail. Is this a good reason to use shared_ptr instead in this scenario or is there a clean way around this?

UPDATE

ApplicationService is a class that is a wrapper around global functions needed to interact with a 3rd party library that my code uses. I have this class as I believe it's the standard way to support unit testing and stubbing/mocking of free standing functions. This class simply delegates calls to the corresponding global functions. The call appService_.Destroy(something); is actually destroying an object used by each specific instance of MyClass not destroying anything do with the Application class itself.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

池予 2024-12-20 10:36:22

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

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

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

编辑:根据你的更新(以及我对something论点的愚蠢忽视),你的设计似乎确实相当正确(至少如果你不能乱改变ApplicationService),抱歉。

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

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

当然,仍然完全不需要使用动态分配,更不用说 shared_ptr 了。尽管嵌套块对代码造成了一些影响,但这并不能阻止以非动态方式且毫无理由地应用动态分配的丑陋,而且它至少“在语义上看起来不错” customAppService块顶部的声明;)

The answer is: you don't need to know, as your design is broken, anyway.

First, a Destroy sounds like a bad idea, furthermore if called in an object that is not responsible for the destruction of the other object. The code from the Destroy method belongs into ApplicationService's destructor (which is hopefully virtual, although in this case it doesn't actually need to), which in contrast to C# gets called at a perfectly determined point in time.

Once you've done this, you will (hopefully) realize, that it is not the responsibility of MyClass to destroy the appService_, as it does not own it. It is the responsibility of the ConsumerClass (or rather the DoSomething method), which really manages the actual service and which does actually destroy it automatically once you've moved Destroy's code into the destructor. Isn't it nice how RAII does make happen everything in a clean and automatic way?

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
    }
}

This is IMHO the perfect clean C++ way around it and the problem is definitely not a reason to spam shared_ptrs. Even if you really need a dedicated Destroy method and cannot move the code into the destructor (which I would take as a motivation for overthinking the design), then you would still call Destroy from DoSomething as again, MyClass is not responsible for destroying the appService_.

EDIT: According to you update (and my stupid overlooking of the something argument), your design seems indeed quite correct (at least if you cannot mess with changing the ApplicationService), sorry.

Allthough class members should get destroyed in reverse order of construction, I'm not sure this also holds for local automatic variables. What you could do to make sure the destructors get called in a defined order is introduce nested scopes using simple blocks:

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

Of course there is still completely no need to use dynamic allocation, let aside shared_ptrs. Although the nested blocks blow the code a bit, it is nothing against the ugliness of dynamic allocation applied in a non-dynamic way and without reason and it at least "looks nice in a semantic way" with customAppService's declaration on top of the block ;)

一身仙ぐ女味 2024-12-20 10:36:22

在 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. ]

In C++, objects, in general, are destroyed in the order that is exact opposite of the order they were created in.

Based on your example, MyClass will be destroyed before CustomApplicationService

The exception is when a destructor is called explicitly. However, I don't think you should concern yourself with this exception at this stage.

Another subtlety is called static initialization order fiasco . However, this does not apply to automatic (stack) variables.

Edit:
From C++2003 - looked for 'reverse order'

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. ]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文