views:

248

answers:

3

Storing objects in heterogeneous vector with stack-allocated objects

Hello,

Say I have an abstract class CA, derived into CA1, CA2, and maybe others.

I want to put objects of these derived types into a vector, that I embbed into a class CB. To get polymorphism right, I need to store a vector of pointers:

class CB
{
    std::vector <CA*> v;
};

Now, say I have the following main function:

int main()
{
    CB b;
    CA1 a1;
    CA2 a2;
    b.Store( a1 );
    b.Store( a2 );
}

How do I write the method void CB::Store(const CA&) in a simple way, so the stored objects survive when the original objects gets destroyed (which doesn't occur in the simple example above).

My problem is that I need to first copy objects on the heap before copying their adress in the vector, but how can I create an object of a derived type ? Sure, I could use RTTI, and search for all possible types, create and allocate a pointer, and copy (with proper casting) the object into the allocated space before pushing it into the vector. But this seems quite complicated, no ?

Is there a simpler way ?

(And without using dynamic allocation in the main !)

+1  A: 

Sounds like you need a clone() function in your abstract class that your derived classes will implement.

class CA
{
   public:
   virtual ~CA() {}
   virtual CA* clone() const = 0;
}

class CA1 : public CA
{ 
    public:
    virtual CA *clone() const
    {
       return new CA1(*this);
    }
};
RC
I think you missed an inheritance relationship between CA and CA1
mocj
...and a virtual destructor and a "public:" ;-)
sellibitze
+4  A: 

Commonly, you will provide a clone function:

struct CA
{
    virtual CA *clone(void) const = 0;
    virtual ~CA() {} // And so on for base classes.
}

struct CA1 : public CA
{
    virtual CA *clone(void) const
    {
        return new CA1(*this);
    }
}

struct CA2 : public CA
{
    virtual CA *clone(void) const
    {
        return new CA2(*this);
    }
}

This is called a virtual constructor, you can construct copies of objects at runtime:

void CB::Store(const CA& pObject)
{
    CA *cloned = pObject.clone();
}

You should consider using the Boost.Pointer Container library. Your code would be:

boost::ptr_vector<CA> objects;

void CB::Store(const CA& pObject)
{
    objects.push_back(pObject->clone());
}

And now you have no need to manage memory yourself. The library also respects clone functions, and will call it when making copies of your objects. Tutorial here.

GMan
I think you missed an inheritance relationship between CA, CA1 and CA2
mocj
Where's the virtual destructor?
sellibitze
Oops, lol. The inheritance was dummyness on my part. I leave details like virtual destructors out on purpose because it gets in the way of the answer.
GMan
Very nice solution, thank's, I learned something today !And I'll check out Boost pointer library.
kebs
+1  A: 

A possibility would be to templatize Store on the type of its argument:

class CB
{
public:
    template<class T>
    void Store(const T& t)
    {
         v.push_back(new T(t));
    }

private:
    std::vector <CA*> v;
};

A warning though: Unlike the "clone()" solution posted by others, this is prone to slicing. For example, this works fine:

CB b;
CA1 a1;
CA2 a2;
b.Store(a1);
b.Store(a2);

But this does not:

CA1 a1;
CA* a = &a1;
b.Store(*a); //Ouch! this creates a new CA, not a CA1

Giving a protected copy ctor to CA prevents such misuse. However, if we further subclass CA1, the issue comes back.

Éric Malenfant