Click here to Skip to main content
15,885,985 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
Hi,
I have a class MyClass which implements an interface MyInterface:
C++
class MyInterface {
public:
   virtual void myFunction () = 0;
};

class MyClass : public MyInterface {
public:
    MyClass ();
    virtual ~MyClass ();
    void myFunction ();
};

Now I want to make a dynamic array of MyClass objects.
My first attempt was:
C++
MyInterface *a;
a = new MyClass[n];
for (int i=0; i<n; i++) {
    a[i].myFunction ();
}

which failed at runtime for n > 1.

So I come to a possible solution like this:
(A)
C++
MyInterface **a;
a = new MyInterface*[n];
for (int i=0; i<n; i++) {
    a[i] = new MyClass ();
    a[i] ->myFunction ();
}

The problem with this solution is that when I want to reclaim the used resources with:
C++
for (int i=0; i<n; i++) {
    delete a[i];
}
delete []a;
it really takes a long time, if compared to:
(B)
C++
MyClass *a;
a = new MyClass[n];
for (int i=0; i<n; i++) {
    a[i].myFunction();
}
delete []a;


I notice that allocating with (A) takes a lot more memory than (B), anyone can explain this?

Thanks.
Posted

If you are using polymorphism then you have to use a pointer or reference, you can not achieve polymorphism with values.
In this case:
C++
MyInterface *a;
a = new MyClass[n];
for (int i=0; i<n; i++) {
    a[i].myFunction ();
}

Your MyInterface *a "array" stores the the class instances themselves as values. Your assignment a = new MyClass[n]; compiles (compiles - not works) just because you explited a relatively dangerous trait of C, that is array identifiers of type T and pointers to type T are exchangeable and auto-casted in most situations (except for a few cases with templates and function parameters).

Both solution A and B are correct though. Your A case takes much more memory because you allocated a lot of small chunks instead of one big chunk and that can lead to memory fragmentation if you regularly free/allocate chunks but in your case the extra wasted space probably comes from memory usage of your allocator that uses some extra memory per allocated block to keep track of allocated/free space in your heap.

However if you need to provide MyInterface **a then you can mix solution A and B as an optimization, you allocate both a MyInterface **a like in solution A and a MyClass *b like in solution B and then later you can fill in the a interface pointers to point to the items of the b array.

EDIT: Forgot to mention something that is much more like an answer to your problem:
C++
class Base
{
public:
    Base(int base_data=0)
        : m_BaseData(base_data)
    {
    }
private:
    int m_BaseData;
};

class Derived : public Base
{
public:
    Derived(int derived_data=0)
        : m_DerivedData(derived_data)
    {
    }
private:
    int m_DerivedData;
};

void test()
{
    // EXAMPLE #1
    const int ARRAY_SIZE = 5;
    Base* base_array = new Base[ARRAY_SIZE];
    Derived* derived_array = new Derived[ARRAY_SIZE];
    printf("base_array member_size: %d\n", (int)((char*)&base_array[1]-(char*)&base_array[0]));
    // note: base_array member_size is 4 on my machine
    printf("derived_array member_size: %d\n", (int)((char*)&derived_array[1]-(char*)&derived_array[0]));
    // note derived_array member_size is 8 on my machine

    base_array = new Derived[ARRAY_SIZE];
    // note that the first (index 0) member pointed by base_array will be accessed perfectly
    // because the assignment performed a static cast (!!!) and adjusted the pointer (by offsetting
    // it if needed). However if you perform an indexing on a (Base*) type then you will be
    // accessing a memory area that is 4 bytes (base_array member_size) past the current value
    // of the pointer and this gives a wrong result since you allocated a Derived array whose
    // members are 8 bytes large. For this simple reason you MUST always assign a dynamically
    // allocated C++ array to exactly same type of pointer as the type of the members in the array!!!

    // EXAMPLE #2
    Derived derived;
    // This is another common mistake related to inheritance and storing instances by value:
    // Copying only the base of a larger object that is an instance of a class derived from the base class.
    // This is perfectly valid in many cases but sometimes it causes a bug, sometimes you store
    // data in the base part of the derived class that is not valid without the derived parts of
    // the object. To disable copying just the base part of an instance put the base class copy
    // constructor to protected.
    Base base(derived);
    // OR
    Base base2 = derived;
}
 
Share this answer
 
v5
Comments
fjdiewornncalwe 21-Sep-12 14:56pm    
well explained. +5
pasztorpisti 21-Sep-12 14:58pm    
Thank you!
momond19 21-Sep-12 15:05pm    
thank you for the explanation
it clears up everything :)
pasztorpisti 21-Sep-12 15:35pm    
I'm glad I could help! Cheers!
pasztorpisti 21-Sep-12 15:40pm    
One more thing I forgot to mention: if you dont use the correct pointer type to hold the created array then your delete[] also behaves incorrectly because it wants to delete an array of baseclass instances, this at worst (and most probably) leads to a crash.
Solution using std::vector...

Declare this somewhere
C++
std::vector<myinterface *> InterfaceList;


To add an interface...
C++
MyInterface *pNewInterface;
pNewInterface = new MyClass;
if (pNewInterface)
   InterfaceList.push_back(pNewInterface);


To iterate through the interfaces
C++
size_t n = InterfaceList.size();
for (size_t i=0; i < n; i++) {
    InterfaceList[i]->myFunction();
}


Then to delete them later...
C++
size_t n = InterfaceList.size();
for (size_t i=0; i < n; i++) {
    delete InterfaceList[i];
}
InterfaceList.clear();
 
Share this answer
 
v6
Comments
momond19 21-Sep-12 15:12pm    
thanks for your answer
JackDingler 21-Sep-12 15:23pm    
I made some corrections. I was fighting the copy and paste mechanism previously...

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