Reputation: 1041
It is known that treating items of an array polymorphically is dangerous because, in general, base and derived classes have different sizes, and the pointer arithmetic will most likely fail.
void doSomethingOnBases(Base* bases, int numBases)
{
for(int i = 0; i < numBases; i++)
{
bases[i].doSomething();
}
}
//...
//...
Derived deriveds[60]; // declare 60 derived objects
doSomethingOnBases(deriveds, 60); // will crash and burn
//...
//...
But what if, instead, I did this?
template <class X>
void doSomethingOnArray(X* arr, int numItems)
{
for(int i = 0; i < numItems; i++)
{
arr[i].doSomething();
}
}
//...
//...
Derived deriveds[60]; // declare 60 derived objects
doSomethingOnArray(deriveds, 60);
//...
//...
In this case, the compiler will see Derived*
being passed in and generate doSomethingOnArray(Derived*, int)
which is an exact type match, therefore, no pointer arithmetic errors.
If I can guarantee that whatever object is de-referenced in doSomethingOnArray()
has a doSomething()
method, is this a safe way to fake polymorphism?
Upvotes: 1
Views: 61
Reputation: 15951
As long as you do it like in your second example and not, e.g., like
doSomethingOnBases<Base>(deriveds, 60);
you should be fine. If you want this to be specific to Arrays, consider writing your template like this:
template <typename X, std::size_t N>
void doSomethingOnArray(X (&arr)[N])
{
for(auto a = &arr[0]; a != &arr[0] + N; ++a)
a->doSomething();
}
The advantage of this version is that it cannot really be called incorrectly. It can only be used on actual arrays and it infers the size and element type of the array automatically from the argument.
Also, note that you could simply be using a range-based for loop
for (auto& a : arr)
a->doSomething();
or
for (Base& b : arr)
…
if you really need a base class reference for some reason…
Upvotes: 1
Reputation: 5486
Using templates is possible, but another solution would be to pass a Base** bases
where you hold an array of pointers to a objects derived from Base.
void doSomethingOnBases(Base** bases, int numBases)
{
for(int i = 0; i < numBases; i++)
{
bases[i]->doSomething();
}
}
This would allow you to keep an array of mixed derived objects and not just a single type Derived
array.
BTW: Note that if you are going with a templated solution then you don't even need the inheritence from Base.
Upvotes: 2