C++: Should class own or observe its dependencies

cdependency-injectionsmart-pointer

Say I have a class Foobar that uses (depends on) class Widget. In good ol' days, Widget wolud be declared as a field in Foobar, or maybe as a smart pointer if polymorphic behavior was needed, and it would be initialized in constructor:

class Foobar {
    Widget widget;
    public:
    Foobar() : widget(blah blah blah) {}
    // or
    std::unique_ptr<Widget> widget;
    public:
    Foobar() : widget(std::make_unique<Widget>(blah blah blah)) {}
    (…)
};

And we'd be all set and done. Unfortunately, today, Java kids will laugh at us once they see it, and rightfully, as it couples Foobar and Widget together. The solution is seemingly simple: apply Dependency Injection to take construction of dependencies out of Foobar class. But then, C++ forces us to think about ownership of dependencies. Three solutions come to mind:

Unique pointer

class Foobar {
    std::unique_ptr<Widget> widget;
    public:
    Foobar(std::unique_ptr<Widget> &&w) : widget(w) {}
    (…)
}

Foobar claims sole ownership of Widget that is passed to it. This has following advantages:

  1. Impact on performance is negligible.
  2. It's safe, as Foobar controls lifetime od its Widget, so it makes sure that Widget doesn't suddenly disappear.
  3. It's safe that Widget won't leak and will be properly destructed when no longer needed.

However, this comes at a cost:

  1. It places restrictions on how Widget instances can be used, e.g. no stack-allocated Widgets can be used, no Widget can be shared.

Shared pointer

class Foobar {
    std::shared_ptr<Widget> widget;
    public:
    Foobar(const std::shared_ptr<Widget> &w) : widget(w) {}
    (…)
}

This is probably closest equivalent ot Java and other garbage collected languages. Advantages:

  1. More universal, as it allows sharing dependencies.
  2. Maintains safety (points 2 and 3) of unique_ptr solution.

Disadvantages:

  1. Wastes resources when no sharing is involved.
  2. Still requires heap allocation and disallows stack-allocated objects.

Plain ol' observing pointer

class Foobar {
    Widget *widget;
    public:
    Foobar(Widget *w) : widget(w) {}
    (…)
}

Place raw pointer inside class and shift the burden of ownership to someone else. Pros:

  1. As simple as it can get.
  2. Universal, accepts just any Widget.

Cons:

  1. Not safe anymore.
  2. Introduces another entity that is responsible for ownership of both Foobar and Widget.

Some crazy template metaprogramming

The only advantage I can think of is that I'd be able to read all those books I didn't find time for while my software is builing;)

I lean towards the third solution, as it is most universal, something has to manage Foobars anyway, so managing Widgets is simple change. However, using raw pointers bothers me, on the other hand smart pointer solution feel wrong to me, as they make consumer of dependency restrict how that dependency is created.

Am I missing something? Or is just Dependency Injection in C++ not trivial? Should class own its dependencies or just observe them?

Best Answer

I meant to write this as a comment, but it turned out to be too long.

How I know that Foobar is the only owner? In the old case it's simple. But the problem with DI, as I see it, is as it decouples class from construction of its dependencies, it also decouples it from ownership of those dependencies (as ownership is tied to construction). In garbage collected environments such as Java, that's not a problem. In C++, this is.

Whether you should use std::unique_ptr<Widget> or std::shared_ptr<Widget>, that is up to you to decide and comes from your functionality.

Let's assume you have a Utilities::Factory, which is responsible for the creation of your blocks, such as Foobar. Following the DI principle, you will need the Widget instance, to inject it using Foobar's constructor, meaning inside one of the Utilities::Factory's method, for example createWidget(const std::vector<std::string>& params), you create the Widget and inject it into the Foobar object.

Now you have a Utilities::Factory method, which created the Widget object. Does it mean, the method should me responsible for its deletion? Of course not. It is only there to make you the instance.


Let's imagine, you are developing an application, which will have multiple windows. Each window is represented using the Foobar class, so in fact the Foobar acts like a controller.

The controller will probably make use of some of your Widgets and you have to ask yourself:

If I go on this specific window in my application, I will need these Widgets. Are these widgets shared among other application windows? If so, I shouldn't be probably creating them all over again, because they will always look the same, because they are shared.

std::shared_ptr<Widget> is the way to go.

You also have an application window, where there is a Widget specifically tied to this one window only, meaning it won't be displayed anywhere else. So if you close the window, you don't need the Widget anywhere in your application anymore, or at least its instance.

That's where std::unique_ptr<Widget> comes to claim its throne.


Update:

I don't really agree with @DominicMcDonnell, about the lifetime issue. Calling std::move on std::unique_ptr completely transfers the ownership, so even if you create an object A in a method and pass it to another object B as a dependency, the object B will now be responsible for the resource of object A and will correctly delete it, when object B goes out of scope.