Polymorphism on class instance is not directly applicable to array of class instances

Below code produces run-time segmentation fault (core dumped) when execution reached line 53: delete [] array

Code 1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <cstdlib>
#include <iostream>
using namespace std;

#define QUANTITY 5


class Parent
{
    protected:
        int ID;

    public:
        virtual void showID(){ cout << "Parent::showID()" << endl; }
};

class A : public Parent
{

    public:
        A()
        {
            this->ID = rand();
            cout << "Constructor of A:  Instance created with ID=" << this->ID << endl;
        }

        ~A()
        {
            cout << " ~Destructor of A:  Instance destroyed which ID="  << this->ID << endl;
        }

        void showID()
        {
            cout << "    A::showID() -- ID is " << this->ID << endl;
        }

};


Parent *array;


int main()
{


    array = new A[QUANTITY];

    for (int i=0; i < QUANTITY; i++)
        array[i].showID();

    cout << "Try to delete [] array.." << endl;
    delete [] array;

    return 0;
}


Output of code 1:

Constructor of A:  Instance created with ID=1804289383
Constructor of A:  Instance created with ID=846930886
Constructor of A:  Instance created with ID=1681692777
Constructor of A:  Instance created with ID=1714636915
Constructor of A:  Instance created with ID=1957747793
    A::showID() -- ID is 1804289383
    A::showID() -- ID is 846930886
    A::showID() -- ID is 1681692777
    A::showID() -- ID is 1714636915
    A::showID() -- ID is 1957747793
Try to delete [] array..
Segmentation fault (core dumped)




Question:
Why does segmentation fault happen in code 1 above?


Last edited on
Then, I just change the line 40 above to:

A *array;

then everything works fine.

The output is as below:
Constructor of A:  Instance created with ID=1804289383
Constructor of A:  Instance created with ID=846930886
Constructor of A:  Instance created with ID=1681692777
Constructor of A:  Instance created with ID=1714636915
Constructor of A:  Instance created with ID=1957747793
    A::showID() -- ID is 1804289383
    A::showID() -- ID is 846930886
    A::showID() -- ID is 1681692777
    A::showID() -- ID is 1714636915
    A::showID() -- ID is 1957747793
Try to delete [] array..
 ~Destructor of A:  Instance destroyed which ID=1957747793
 ~Destructor of A:  Instance destroyed which ID=1714636915
 ~Destructor of A:  Instance destroyed which ID=1681692777
 ~Destructor of A:  Instance destroyed which ID=846930886
 ~Destructor of A:  Instance destroyed which ID=1804289383


Why does segmentation fault happen in code 1 above, and how to solve it if I insist to use Parent *array; in line 40 ?
Last edited on
Parent (like any base class) needs a public virtual destructor.

See for example http://www.gotw.ca/publications/mill18.htm guideline 4

PS: Oh, and although that might make this particular program run without crashing, Disch below is absolutely correct.
Last edited on
I don't think any of the replies so far have addressed the actual problem here.

The problem is this:

 
Parent* array = new A[QUANTITY];  // THIS IS BAD 


Polymorphism allows for a Parent* to point to a single A object... however it cannot point to an array of A's because the objects have different sizes. By doing this, only the first pointer in your 'array' will be valid. All other pointers will become corrupt... and therefore will likely segfault/crash when deleted.

To explain why this is the case....the compiler uses the pointer type to do some "behind the scenes" pointer math whenever you access elements. Here's an example:

1
2
3
4
5
int* example = new int[5];

cout << "[0] = " << &example[0] << "\n";
cout << "[1] = " << &example[1] << "\n";
cout << "[2] = " << &example[2] << "\n";


Assuming sizeof(int)==4 you will notice that each value printed will be 4+the previous. So output might look like this:

[0] = 00010000
[1] = 00010004
[2] = 00010008


The compiler is using sizeof(int) because example is an int pointer. If example was a double pointer, it would use sizeof(double).


Coming back to your example with a Parent and Child class:

1
2
3
4
5
Parent* ptr = new Parent[5];

cout << "[0] = " << &ptr[0] << "\n";
cout << "[1] = " << &ptr[1] << "\n";
//... 


Assuming sizeof(Parent)==0x10, this might output something like:

[0] = 00010000
[1] = 00010010
[2] = 00010020
...


Again, because the compiler is using sizeof(Parent) to determine where further elements are.

Another example with the child class 'A':
1
2
3
4
5
A* ptr = new A[5];

cout << "[0] = " << &ptr[0] << "\n";
cout << "[1] = " << &ptr[1] << "\n";
//... 


where sizeof(A)==0x20

[0] = 00010000
[1] = 00010020
[2] = 00010040


Again, what you'd expect.


The thing to note here is that sizeof(A) is always going to be >= sizeof(Parent) because an A will contain everything its Parent does, and also possibly some additional information specific to A.

With this in mind... take a look at what you're doing:
1
2
3
4
5
Parent* ptr = new A[5];  // BAD BAD BAD

cout << "[0] = " << &ptr[0] << "\n";
cout << "[1] = " << &ptr[1] << "\n";
//... 


[0] = 00010000
[1] = 00010010
[2] = 00010020


Since 'ptr' is a Parent pointer, the compiler thinks each element is sizeof(Parent) bytes. However they're actually sizeof(Child) bytes. As a result... &ptr[1] is a bad pointer! It doesn't point to an object, it actually points to memory smack in the middle of the first element.



how to solve it if I insist to use Parent *array; in line 40 ?


You can't. You don't have an array of Parents, you have an array of As. You must use the correct pointer type.
Last edited on
^ Not that it matters, but I've got sizeof(Parent) = sizeof(A) = 16
Implementation/compiler dependent. Besides it's a conceptual programming flaw. Even if the sizes are the same it's not something that you should be doing.
Disch, thank you very much!
Your explanation is indeed logical and good !

As conclusion, polymorphism doesn't allow Parent* pointing to an array of A.
So I have to use a pointer array instead, as follows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Parent *array[QUANTITY];    // this is pointer array


int main()
{
    for (int i=0; i < QUANTITY; i++)
        array[i] = new A;

    for (int i=0; i < QUANTITY; i++)
        array[i]->showID();

    for (int i=0; i < QUANTITY; i++)
        delete array[i];

    return 0;
}


This produces a run-time output that seems ok.
Is this a correct approach to handle this issue..?
Last edited on
That is correct. Your approach there is very typical.
Thank you very much. This really help!

I have another question posted in another thread.
If you have time let's have a look:
http://www.cplusplus.com/forum/general/96327/
Last edited on
Topic archived. No new replies allowed.