views:

846

answers:

3

Hi,

I have an abstract class defining a pure virtual method in c++:

class Base
{
Base();
~Base();

virtual bool Test() = 0;
};

I have subclassed this with a number of other classes (which provide an implementation for Test()), which I'll refer to as A, B, C, etc. I now want to create an array of any of these types using this base class:

int main(int argc, char* argv[])
{
    int size = 0;
    Base* bases = new Base[10];

    bases[size++] = new A();
    bases[size++] = new B();

    for (int i = 0; i < size; i++)
    {
        Base* base = bases[i];
        base->Test();
    }
}

(Excuse any errors I might have made, I'm writing this on the fly to provide a simple example).

The problem is I can't instantiate the array as it would require creating an instance of the Base class (which it can't do as it's abstract). However, without doing this, it hasn't allocated the memory needed to assign to the indices of the array, and thus provides a segmentation fault when trying to access that memory. I am under the impression that it's not good practice to mix new and delete with malloc and free.

It may be that I have confused the way this should be used and I should be attempting to use templates or some other mechanism to do this, but hopefully I've provided enough information to illustrate what I'm attempting to do.

So what is the best way of doing this and how would I get around this problem of allocating memory to an abstract class?

Thanks, Dan

+11  A: 

There is only a slight misunderstanding in that code. Instead of allocating Base objects, you have to allocate pointers. A pointer can exist at any time. A pointer to a abstract class, to an incomplete type, and even to void is valid:

int main(int argc, char* argv[])
{
    int size = 0;
    Base** bases = new Base*[10];

    bases[size++] = new A();
    bases[size++] = new B();

    for (int i = 0; i < size; i++)
    {
        Base* base = bases[i];
        base->Test();
    }
}
Johannes Schaub - litb
Great answer. I'd throw in some defensive programming.. Set all the pointers to NULL. memset(bases, NULL, sizeof(Base*)*10); Just to protect myself. Great answer though.
baash05
baash05, thanks for the praise :) new Base*[10](); is enough to nullify them all. i like that feature of new.
Johannes Schaub - litb
Not exception safe. Use boost:ptr_vector<Base> rather than an array.
Martin York
Now that I look at you also leak!
Martin York
http://uk.youtube.com/watch?v=wRrunDtDqSQ have fun :)
Johannes Schaub - litb
+6  A: 

First, make sure that your destructor is also declared as virtual:

virtual ~Base();

You're better off storing a an array of pointers to instances:

Base** bases = new Base *[10];
Ates Goral
+4  A: 

An array relies on all of its element being of the same size. When you have C++ classes derived from the same base, the instances can be any size at all. So, no matter whether the base is abstract or not, you can't allocate array of base instances, and then put derived instances there.

As other answers say, you need to use pointers. Allocate an array of pointers to base, and then allocate various derived instances and store pointer to them in the array.

Arkadiy