I am writing a piece of code with high demands on performance where I need to handle a large number of objects in a polymorphic way. Let's say I have a class A and a class B which is derived from A. I could now create a vector of B:s like this
vector<A*> a(n);
for(int i = 0; i < n; i++)
a[i] = new B();
but if n is large (in the order 10^6 or more in my case) this would require very many calls to new and moreover the n objects could potentially be spread out all over my main memory resulting in very poor cache performance. What would be the right way to deal with this situation? I am thinking of doing something like the following to have all the objects in a contiguous memory region.
B* b = new B[n];
vector<A*> a(n);
for(int i = 0; i < n; i++)
a[i] = b + i;
but one problem is how to free up the memory allocated by new B[n] if b is not available anymore (but we still have a). I have just learnt that trying
delete[] a[0];
is not a good idea...
You just have to store the pointers returned from new[] somewhere, and delete[] them later. Another vector, for example.
You can use placement new to construct an object at a particular memory location:
But you cannot solve the 'real' problem without giving up the continuous storage: if one object is freed, it will cause a hole in the heap, thus causing high fragmentation over time. You could only keep track of the freed objects and re-use the storage.
If you know for sure that those will only be objects of type
B
why not use a parallel vector:If you want to keep all your objects in contiguous memory and, at the same time, avoid using an indirection (a vector of base class pointers), you can use a union-style container, e.g. a vector
boost::variant
. This will, of course, assume that there is a limited and known number of derived classes and that their sizes are comparable. The drawback is that each element of the vector will take as much memory as the biggest derived class, regardless of their actual class (and it also assumes your classes are reasonable cheap to copy). The advantages are that you have contiguous heterogeneous storage of the polymorphic objects, type-safety, and no indirection when accessing elements. Here is a basic example withboost::variant
and three classesA
,B
,C
, where bothB
andC
inherit fromA
, and they are all polymorphic (and, of course, this could be much nicer with some sugar-coating and/or something more specialized for your purpose, notboost::variant
which is not really appropriate for this purpose):