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:
- Impact on performance is negligible.
- It's safe, as
Foobar
controls lifetime od itsWidget
, so it makes sure thatWidget
doesn't suddenly disappear. - It's safe that
Widget
won't leak and will be properly destructed when no longer needed.
However, this comes at a cost:
- It places restrictions on how
Widget
instances can be used, e.g. no stack-allocatedWidgets
can be used, noWidget
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:
- More universal, as it allows sharing dependencies.
- Maintains safety (points 2 and 3) of
unique_ptr
solution.
Disadvantages:
- Wastes resources when no sharing is involved.
- 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:
- As simple as it can get.
- Universal, accepts just any
Widget
.
Cons:
- Not safe anymore.
- Introduces another entity that is responsible for ownership of both
Foobar
andWidget
.
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?
std::unique_ptr
is the way to go. You can usestd::move()
to transfer the ownership of a resource from upper scope to the class.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.