Search code examples
c++arraysinheritanceterminologypointer-arithmetic

Should this be called some special case of object slicing?


Let's say I have a class Derived which derives from class Base whereas sizeof(Derived) > sizeof(Base). Now, if one allocates an array of Derived like this:

Base * myArray = new Derived[42];

and then attempts to access the n-th object using

doSomethingWithBase(myArray[n]);

Then this is might likely (but not always) cause undefined behaviour due to accessing Base from an invalid location.

What is the correct term for such an programming error? Should it be considered a case of object slicing?


Solution

  • 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_ptrs 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());