So, my Business Code needs some Objects.
It does not know how much objects it needs and it does not know the exact types (because polymorphism is involved).
For me, that sounds for a good reason to go factory pattern.
My code now looks like:
std::vector<AbstractBaseClass*> objectList;
Factory f;
objectList = f.create("path/to/config.txt");
The prototype of the factory's create-method looks like:
std::vector<AbstractBaseClass*> Factory ::create(std::string configFile)
Good news, its working!
The Factory reads the config, and then decides how many objects to create and which concrete types they have.
Well, I did a lot of searching but I couldn't find an example of a factory returning not a single object but a container. Thus, my question: is this good style?
I think yes because this way the whole parsing/creation process is hidden from the business logic. The logic only knows it has some container with a bunch of objects. But maybe you have other opinions?
Please note: this project is all about learning good OOP habits.
Ok, imagine this approach is OK.
I learned, raw pointers are evil. (OK, they are not evil per definition, but I try to avoid them).
So, I want to move to some smart pointers. Lacking boost
and C++11
on this machine I'm starting with auto_ptr
.
OK, new approach:
std::vector< auto_ptr<AbstractBaseClass> > objectList;
Factory f;
objectList = f.create("path/to/config.txt");
And Factory:
std::vector< auto_ptr <AbstractBaseClass> > Factory ::create(std::string configFile)
This looks evil.
And it doesn't compile because at the moment, I'm getting some crazy STL compiler errors.
But, imagin' it compiles.
Is this good?
I've never seen such a construct – a factory returning a container of smart pointers.
And because I'm not that expert, I want to ask what you think about this.
On a related note, what smart pointer shall I use?
The business logic is the only owner of the objects, so I guess unique_ptr.
However, I'm not sure if a unique_ptr container can be returned.
shared_ptr is easier to implement, I think.
Best Answer
If you use C++ as a mostly OOP language, you'll have to deal with pointers in some form or another. The answer to almost all pointer problems is
std::unique_ptr
, because it has fairly value-like semantics while still enabling you to use polymorphism. Its only overhead is syntactic clutter. This is a lot better thanstd::shared_ptr
because this has additional overhead for reference counting.std::auto_ptr
because it has confused semantics: copying behaves like moving, which also requires that the copy source is notconst
so that the moved pointer can be erased from the source. (Also note that the copy assignment operator forstd::vector
expects a const reference, sovector<auto_ptr<T>>
is non-copyable.)unique_ptr
field since that takes care of all necessary resource management.However, using a Pimpl as Bridge Pattern can enable you to design a polymorphic class hierarchy with a value-based API but pointer-like semantics. In particular, you could use
vector<BaseClass>
as a more convenient notation forvector<unique_ptr<BaseClassIf>>
.The “problem” with
std::unique_ptr
is that it requires C++11. The core idea of this pointer type is that it can't be copied, but it can be moved. Therefore, ownership is always clearly defined. When you return an object by value, it will be subject to copy semantics (even if the actual copy might be optimized away), except in C++11 where move semantics will allow you to return anunique_ptr
by value.In general, using a container of smart pointers is perfectly fine, and better than the alternatives. In your case, it fails because of the restricted semantics of C++03 with regards to
auto_ptr
. If you can't upgrade to C++11 (which is supported in all current mainstream compilers), you have two realistic choices: use raw pointers, or wrap the pointer in a custom Pimpl. I'd use the Pimpl if the effort is justifiable. It isn't tremendously complicated code, but you have to be careful to forward all necessary operations:Note that the interface must make provisions to access the copy constructor, since the exact type is unknown by the
BaseClass
wrapper. This is an occasionally useful technique (e.g. as “type erasure” to hide template parameters), but here it's just annoying fallout from using polymorphism. Also note that it is not possible to define a copy assignment operator for the adapter, unless you include a commonvirtual BaseClassIf::operator=(const BaseClassIf&)
method in your interface – but most object hierarchies cannot do an useful copy from their common base.