One important possibility that you've missed is the injection of factories, rather than the class itself. One advantage of this is that it allows your ownership of instances to be cleaner. Note the code is a little uglier since I'm explicitly giving the container ownership of its components. Things can be a little neater if you use shared_ptr
, rather than unique_ptr
, but then ownership is ambiguous.
So starting with code that looks like this:
struct MyContainer {
std::unique_ptr<IFoo> foo;
MyContainer() : foo(new Foo() ) {};
void doit() { foo->doit(); }
}
void useContainer() {
MyContainer container;
container.doit();
}
The non-factory version would look like this
struct MyContainer {
std::unique_ptr<IFoo> foo;
template<typename T>
explicit MyContainer(std::unique_ptr<T> && f) :
foo(std::move(f))
{}
void doit() { foo->doit(); }
}
void useContainer() {
std::unique_ptr<Foo> foo( new Foo());
MyContainer container(std::move(foo));
container.doit();
}
The factory version would look like
struct FooFactory : public IFooFactory {
std::unique_ptr<IFoo> createFoo() override {
return std::make_unique<Foo>();
}
};
struct MyContainer {
std::unique_ptr<IFoo> foo;
MyContainer(IFooFactory & factory) : foo(factory.createFoo()) {};
void doit() { foo->doit(); }
}
void useContainer() {
FooFactory fooFactory;
MyContainer container(fooFactory);
container.doit();
}
IMO being explicit about ownership & lifetime in C++ is important - its a crucial part of C++'s identity - it lies at the heart of RAII and lots of other C++ patterns.