Reputation: 297
I've seen some posts regarding the issue but not something that sums all the options I have at my current situation, and there might be solutions I haven't considered.
My situation is quite general I have a couple of classes Inf1
Inf2
(which are more like interfaces) both have pure virtual methods.
Where Inf1
has a method
(Inf2& or Inf2* or shared_pointer) foo(/** Some info on how to build the object**/) = 0
(this is part of the problem).
In the implementations of Inf1
return various implementations of Inf2
when implementing foo
.
The Inf2
implementations are relatively small so I wouldn't mind returning them by value so they are copy constructed to the result, but I cannot declare foo
to return just Inf2
because then I'd be returning an abstract object.
Any preferable and or creative ways to solve this? :)
Upvotes: 4
Views: 1809
Reputation: 68698
Your question is really about memory management. Polymorphic subclasses of Inf2
can be arbitrarily large, and the largest isn't known at compile-time, so you can't store them with automatic storage duration, therefore they cannot be returned by value - so you need to manage memory for them somehow at least across the return
Where shall objects of an Inf2
subclass type be stored? And when and from where shall implementations of Inf2
be deleted. The answer to this will generally dictate what kind of handle concept to use. For example if you allocate Inf2 implementations in a memory pool then returning Inf2*
is fine. You can also use unique_ptr<Inf2>
if you want to do single ownership, but then you can't copy the handle. You can also use shared_ptr<Inf2>
if you want to copy the handles, but you have to be careful about cycles, and it is slightly less efficient. You can also create a wrapper class Inf2Handle
that does something more sophisticated, such as copy-on-write, or other things.
You mentioned that implementations are small so perhaps a memory pool is the best architecture.
Which is the best choice really depends on the bigger picture of how Inf1
and Inf2
are to be used.
Upvotes: 2
Reputation: 275760
So you have types Inf1
and Inf2
, which are related to each other, and at least one of them contains abstract methods.
You want to return some sub-implementation of Inf2
, but which one is decided at run time.
This means you need run time polymorphism. There are 3 different ways you could approach this.
First, you could return a pointer -- probably a smart pointer, like unique_ptr
, to the common interface. This requires free store (heap) allocation, but makes ownership clear, and is the simplest answer.
Second, you could write a type erasure object which exposes a non-virtual
version of the Inf2
interface, and forwards to some internal details. These internal details will end up using a smart pointer of some kind, or the third solution. The advantage here is that you can hide how you manage memory, and simply expose value semantics with cheap move. The disadvantage is that there is lots of boilerplate.
Third, you could use something like a boost::variant
-- a union
over a set of types with means to guard against accessing the wrong types. While boost
may not directly be used, the design can be mimiced. The idea is that you have some local storage in which you placement new the data, except maybe for larger objects, where you have a smart pointer you use instead. Unlike the second solution, the set of types you support are explicitly listed in your type. This is the hardest solution (if you don't have access to boost
at least), and requires that you have a fixed (at compile time) set of implementations of Inf2
that all users of Inf1
have to have full details on.
As noted, the first solution is the easiest. The costs to the first solution are merely performance based, and those performance hits wouldn't be hard to fix after the fact if you discovered they where real issues. So I would advise solution #1, and then profile to see if the costs involved are too high. If the costs are high, move to solution #3, possibly in a solution #2 wrapper.
Upvotes: 3