Click here to Skip to main content
15,888,908 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Below is the sample code

#include "conio.h"
#include "iostream"

using namespace std;

class CBase
{
public:
    int m_nBaseData;
    virtual void Display() = 0;
};

class CDerived : public CBase
{
public:
    int m_nDerivedData;
    void Display()
    {
        cout<<"CDerived::Display() : "<<m_nBaseData<<endl;
    }
};

void main()
{
    CBase* ptr = new CDerived[2];
    ptr[0].m_nBaseData = 10;
    ptr[1].m_nBaseData = 20;
    ptr[0].Display();
    ptr[1].Display();
    getch();
}


If I remove member variable int m_nDerivedData; from CDerived class then application crash does not occur.
Can anyone explain this?
Posted

Why would you expect that code to work? Arrays and pointers are completely different beasts and you're trying to use them interchangeably.

What you're doing is:

- creating an array of two derived class objects (each object is a v-table and two ints long)

- you're telling the compiler that the pointer to the first of these objects is actually a pointer to a base class object (each object is a v-table and one int long)

- you're then telling the compiler it's actually an array of at least 2 derived class objects.

- to reference the second object the compiler starts reading at an offset of one v-table and one int from the start of where you've told it the first object is (with most compiler's memory layout that will be the start of the uninitialised derived class' member)

- the compiler reads what it thinks the v-table pointer is from the back end of the first object, which is uninitialised memory, dereferences it, looks up the display function and...

- program go bang

So the morals here are:

- Never access arrays through pointers to a different type

- Don't leave data members uninitialised - if you'd stuck a known pattern in your derived class member variable it might have given you a clue as to what was going on.

If you want to do the sort of thing you're trying you're going to need a helper class to do it. Something that dereferences as the derived class but returns a base class pointer.

Cheers,

Ash
 
Share this answer
 
Comments
gonsalvesroger 15-Jan-11 5:20am    
Thanks a lot for detailed explanation.
gonsalvesroger 15-Jan-11 5:22am    
Is it possible for you explain how to use a helper class to resolve this issue?
Espen Harlinn 15-Jan-11 6:11am    
5+ for taking it this far :)
Dalek Dave 15-Jan-11 8:03am    
Good Answer
Aescleal's description of what goes wrong is excellent.

Since the solution for this particular piece of code wasn't mentioned however, I thought I'd add it here.

CBase* ptr = new CDerived[2];


should be

CDerived* ptr = new CDerived[2];
 
Share this answer
 
Aescleal is right, that's a fair description of what's wrong with the implementation.

I guess you would like to see how it could be made to work though.

C++
#include "conio.h"
#include "iostream"
using namespace std;
class CBase
{
public:
    int m_nBaseData;
    virtual ~CBase() {}
    virtual void Display() = 0;
};

class CDerived :
    public CBase
{
public:
    int m_nDerivedData;
    void Display()
    {
        cout<<"CDerived::Display() : "<<m_nBaseData<<endl;
    }
};


void main()
{
    CBase* ptr[2] = {new CDerived(),new CDerived()};

    CBase* ptr1 = ptr[0];
    CBase* ptr2 = ptr[1];

    ptr1->m_nBaseData = 10;
    ptr2->m_nBaseData = 20;

    ptr1->Display();
    ptr2->Display();

    getch();

    delete ptr1;
    delete ptr2;

}


ptr is an array of two pointers to CBase allocated on the stack - no need to delete it.
The instances are allocated dynamically, so deleting them before we exit is good practice.

I guess you wanted to be able to call the Display() function on both instances through your CBase pointers, well this does the trick. It could also be coded like this:
C++
ptr[0]->Display();
ptr[1]->Display();


So your inital code wasn't that far off, and Aescleal gave you a well reasoned explaination on what was wrong with it.

I also added a virtual destructor virtual ~CBase() {} to CBase.

I feel that this is a good practice when developing class hierarchies, since it allows you to implement cleanup logic in derived classes that will be executed during destruction, even if delete is called on a pointer to a parent class.

Regards
Espen Harlinn
 
Share this answer
 
v3
Comments
Dalek Dave 15-Jan-11 8:03am    
Good Call.
Espen Harlinn 15-Jan-11 8:12am    
Thanks Dalek!
Does it crash if you do the following (leaving the class definitions the same)?:
MIDL
CBase *p = new CDerived();
p->m_nBaseData = 10;
p->Display();
 
Share this answer
 
Comments
gonsalvesroger 15-Jan-11 0:53am    
No.
What I have observed is that for first object the virtual table pointer is proper. But for second object it is returning some garbage value.
you should never cast (at this point implicit) a pointer to a virtual array.
1.) sizeof(CBase) == sizeof(vtable*) + sizeof(int)
2.) sizeof(CDerived) == sizeof(CBase) + sizeof(int)
sorry but this array has to be crash from index>0!
 
Share this answer
 
Answers above are fine. Also looks like you have a memory leak.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900