liveBook · Manning

Polymorphic array element

As explained in 100 C++ Mistakes and How to Avoid Them

A polymorphic array element is an array element accessed through a base-class array interface even though the stored elements have a derived class type.

A polymorphic array element is accessed through a base-class array interface even when the stored elements have a derived type. Unlike a single base pointer, a derived array cannot safely substitute for a base array: pointer arithmetic advances by the wrong element size. section 8.10

Mistake: accepting a derived array as a base array

In the example, printArray(const B[]) accepts both B* and D*. The B array prints four zeros, while the D array prints 0, 1, 0, 1 because indexing uses B-sized steps. deleteArray also destroys the derived allocation through the base-array interface, producing only B destructor output. The apparent polymorphism therefore hides incorrect access and cleanup.

A safer incremental correction makes the functions templates. Type deduction preserves the actual element type, so indexing and delete[] use B for B arrays and D for D arrays without pretending that array polymorphism exists.

Listing 8.19 Passing an array with derived elements
const int SIZE = 4;
 
class B {
private:
    int n;
public:
    B(int n=0) : n(n) {}
    ~B() { std::cout << "destroying B\n"; }              #1
    int getN() const { return n; }                       #2
};
 
class D : public B {
    int m;
public:
    D() : B(0), m(1) {}
    ~D() { std::cout << "destroying D\n"; }
};
 
void printArray(const B a[]) {
    for (int i = 0; i < SIZE; ++i)
        std::cout << a[i].getN() << '\n';                #3
}
 
void deleteArray(const B a[]) {                          #4
    delete [] a;
}
 
int main() {
    B* bs = new B[SIZE];
    printArray(bs);
    deleteArray(bs);
 
    D* ds = new D[SIZE];
    printArray(ds);
    deleteArray(ds);
    return 0;
}

Inspect the complete failing program, including the base and derived classes, array-processing functions, and destructor behavior. section 8.10

How 100 C++ Mistakes and How to Avoid Them covers it

How deep it goes
Explains array indexing, element-size stride, incorrect output, and destructor behavior, then demonstrates templated correction.
Examples
Uses B and D arrays, contrasting printed values and destructor messages before and after templating the functions.
What it assumes
Assumes basic classes, inheritance, pointers, arrays, dynamic allocation, and destructors while teaching array pointer arithmetic.
What you can do after
Diagnose misleading base-array interfaces and replace them with type-preserving templated operations.
How it connects
Connects inheritance, polymorphism, the Liskov substitution principle, pointer arithmetic, arrays, and function templates.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 C++ Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.