Click here to Skip to main content
Rate this: bad
good
Please Sign up or sign in to vote.
See more: C++
Hi,
I have a class MyClass which implements an interface MyInterface:
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:
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)
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:
for (int i=0; i<n; i++) {
    delete a[i];
}
delete []a;
it really takes a long time, if compared to:
(B)
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 21-Sep-12 4:30am
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 1

If you are using polymorphism then you have to use a pointer or reference, you can not achieve polymorphism with values.
In this case:
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:
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;
}
  Permalink  
v5
Comments
Marcus Kramer at 21-Sep-12 14:56pm
   
well explained. +5
pasztorpisti at 21-Sep-12 14:58pm
   
Thank you!
momond19 at 21-Sep-12 15:05pm
   
thank you for the explanation
it clears up everything :)
pasztorpisti at 21-Sep-12 15:35pm
   
I'm glad I could help! Cheers!
pasztorpisti at 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.
momond19 at 21-Sep-12 15:53pm
   
hmm...
which one do you mean?
At solution (A)? or (B)?
pasztorpisti at 21-Sep-12 16:04pm
   
None of those, I mean your first bad solution that crashes. delete[] iterates through the array and calls the destructor of all objects and then deallocates the array itself. Since iteration simply doesn't work in your first solution where you assign the MyClass array instance to a MyInterface pointer its a natrual thing that delete[] crashes. I would also mention that you hav to use delete[] to delete an array and not delete because delete doesn't call the destructor of the array members, this a serious bug made by a lot of beginners. Another thing is that delete[] needs to know the size of the array in order to iterate through your items so usually your array creator new statement (new MyClass[X]) allocates a special block that stores the array size in a hidden location (usually on a negative offset relative to the pointer you get back from new). If you assing your array pointer to a base class pointer then that performs a static cast to the first member of the array that in worst case offsets your pointer (usually if you cast within a class hierarchy that contains multiple inheritance) and this way your MyInterface* (the base class) pointer can not anymore be used to retrieve the array size of your MyClass array, so this is another good reason for delete[] to crash or get a wrong array size.
momond19 at 21-Sep-12 16:59pm
   
ah.. i see
thanks again! cheers!
pasztorpisti at 21-Sep-12 19:49pm
   
Just wanted to mention one more thing: In my opinion this array allocation stuff is a redundant unnecessary feature of C++, and its also unsafe as you see. I think its a redundant C++ feature because a dynamic array can be implemented by using other more "atomic" features of C++: raw memory allocation, placement new and placement delete (and starting from C++11 - object moving). With the previous features you can implement a much better array with some more code but that can deal even with resizing and you can put that "complex" array handler code to a template so you have to write it only once and then use it everywhere - you can also create the array so that every object is created by copying a specified default value. One such general purpose array implementation is std::vector as another member answered. However that general implementation isn't always the best solution for several reasons: it contains more than just a pointer and sometimes that isn't suitable, it always preallocates 16 items as a starting capacity, its debug/release versions are not binary compatible, and so on. These bacdraws are not that bad for general purpose use, but somtimes you want something that is much more well suited for a specific problem. In general however you should go with std::vector and not with C++ array allocation because std::vector is much safer and it deallocates the array automatically and can perform resize/default value initialization if necessary. If you are interested in the internals of std::vector and in the way of writing your own container then research the C++ language features I listed and experiment by writing your own dynamic array template and then here is your first challenge: write a dynamic array whose only content is a NULL pointer when its empty! :-) Ignore the previous post if you are too novice for that or if you are just not interested in this stuff, many general problems can be solved quite well just by using the stl libraries. Even if you are interested in writing your own containers its better to take a look at the stl containers first, at least from a user perspective. Their source code is very-very messy if you want to understand things from that - dont be afraid, a custom container can be implemented from much less and well formatted code. :-)
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 2

Solution using std::vector...
 
Declare this somewhere
std::vector<myinterface *> InterfaceList;
 
To add an interface...
MyInterface *pNewInterface;
pNewInterface = new MyClass;
if (pNewInterface)
   InterfaceList.push_back(pNewInterface);
 
To iterate through the interfaces
size_t n = InterfaceList.size();
for (size_t i=0; i < n; i++) {
    InterfaceList[i]->myFunction();
}
 
Then to delete them later...
size_t n = InterfaceList.size();
for (size_t i=0; i < n; i++) {
    delete InterfaceList[i];
}
InterfaceList.clear();
  Permalink  
v6
Comments
momond19 at 21-Sep-12 15:12pm
   
thanks for your answer
JackDingler at 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)

  Print Answers RSS
0 OriginalGriff 584
1 Maciej Los 275
2 DamithSL 233
3 Sergey Alexandrovich Kryukov 209
4 BillWoodruff 200
0 OriginalGriff 6,803
1 Sergey Alexandrovich Kryukov 6,377
2 DamithSL 5,421
3 Manas Bhardwaj 4,841
4 Maciej Los 4,330


Advertise | Privacy | Mobile
Web01 | 2.8.1411023.1 | Last Updated 21 Sep 2012
Copyright © CodeProject, 1999-2014
All Rights Reserved. Terms of Service
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100