I have seen a lot of advice that it is better to do Type object;
than
Type* object = new Type();
in C++ whenever possible—i.e., minimize your use of new
. I understand the rational behind this and appreciate it.
But according to my understanding, to practice dependency inversion requires pointers, e.g.:
Type* object = new Implementation();
where Type
is abstract (i.e. contains at least one pure virtual method) and Implementation
is concrete. It is not possible to do
Type object = Implementation();
because what that means is
Type object;
object = Implementation();
which requires constructing object
as a Type
initially—but that cannot be done, since Type
is abstract.
Is there an inherent tension between the dependency inversion principle and avoiding new
when using C++? If so, what patterns/principles/practices can be used to mitigate this tension?
3
You can absolutely use Dependency Injection without ever using new()
. Polymorphism, which is what you’re talking about here, is realized in C++ using pointers and/or references. Doc Brown already addressed references, let’s talk about pointers.
As an example (assuming that Implementation
is derived from Type
, and Bar
‘s constructor takes a pointer to a Type
):
Implementation imp;
Bar bar(&imp);
The “address of” operator (&
) takes the address of an object, the result is a pointer to the object. This is a different way of getting a pointer to an object (different compared to your use of new
, that is.)
In C++, any time I have a pointer to a Derived
, I can also use it as a pointer to a Base
. That’s the whole idea of polymorphism, a Derived
is a Base
, right?
If my 2-step example above made a leap that you couldn’t follow, I’ll repeat it here, but with one more (unnecessary) step:
Implementation imp;
Type *t = &imp;
Bar bar(t);
t
is a pointer to a Type
, so it can point to any class derived from Type
, including Implementation
. No use of new
. No tension.
5
DIP means instead of instantiating objects of class Foo
directly in class Bar
, you have an abstract interface IFoo
, and pass already constructed objects of type IFoo
into Bar
(for example, through the constructor of Bar
). That allows you easily to replace Foo
objects in Bar
by MockFoo
objects for example, for testing purposes. If those objects are constructed on the stack like
Foo foo;
Bar bar(&foo);
or dynamically like
IFoo *foo = new Foo();
Bar bar(foo);
where Bar
constructor has the signature
Bar(IFoo *foo)
{
//...
}
does not matter in terms of DIP, that decision does only depend on the intended life time of your object foo
(which in both cases should be at least as long as the lifetime of bar
).
The pointer variant is technically different on one aspect: if you like, you can transfer the ownership of the foo
object to bar
and let bar
do the memory cleanup by calling delete
on foo
in the destructor. However, it is questionable if this is good programming style, and I would not recommend that technique to you. If you want to have automatic cleanup of foo
when bar does not need it any more, I suggest that you better make use of smart pointers.
To your edit: I guess you have a misconception here about pointers and “new”. This code
Implementation object;
Type *ptrObject = &object;
gives you a pointer to an object of type Type
without using new
. Or in short:
"Avoiding the usage of new" != "You cannot have pointers"
(and both is irrelevant to the dependency inversion principle).
2
No, dependency inversion does not necessitate pointers.
The decision to use pointers or not is a question of data sharing and memory optimization.
Dependency inversion has to do with class hierarchies and inhertiance.
All the same places where you expect to pass Type*
that was allocated with new Type()
can be changed to accept a Type&
that is created with type = Type()
.
9