This is not object slicing.
As noted, indexing myArray
does not cause object slicing, but results in undefined behavior caused by indexing into an array of Derived
as if it were an array of Base
.
A kind of "array decay bug".
The bug introduced at the assignment of new Derived[42]
to myArray
may be a variation of an array decay bug.
In a true instance of this type of bug, there is an actual array:
Derived x[42];
Base *myArray = x;
The problem is introduced because an array of Derived
decays into a pointer to Derived
with value equal to the address of its first element. The decay allows the pointer assignment to work properly. This decay behavior is inherited from C, which was a language design feature to allow arrays to be "passed by reference".
This leads us to the even worse incarnation of this bug. This feature gives C and C++ semantics for arrays syntax that turn array function arguments into aliases for pointer arguments.
void foo (Base base_array[42]) {
//...
}
Derived d[42];
foo(d); // Boom.
However, new[]
is actually an overloaded operator that returns a pointer to the beginning of the allocated array object. So it is not a true instance of array decay (even though the array allocator is used). However, the bug symptoms are the same, and the intention of new[]
is to get an array of Derived
.
Detecting and avoiding the bug.
Use a smart pointer.
This kind of problem can be avoided by using a smart pointer object instead of managing a raw pointer. For example, the analogous coding error with unique_ptr
would look like:
std::unique_ptr<Base[]> myArray = new Derived[42];
This would yield a compile time error, because unique_ptr
s constructor is explicit
Use a container, and maybe std::reference
.
Alternatively, you could avoid using new[]
, and use std::vector<Derived>
. Then, you would have forced yourself to design a different solution for sending this array to framework code that is only Base
aware. Possibly, a template function.
void my_framework_code (Base &object) {
//...
}
template <typename DERIVED>
void my_interface(std::vector<DERIVED> &v) {
for (...) {
my_framework_code(v[i]);
}
}
Or, by using std::reference_wrapper<Base>
.
std::vector<Derived> v(42);
std::vector<std::reference_wrapper<Base>> myArray(v.begin(), v.end());