Question

Say I have these structs:

struct Base{
 ...
}

struct Derived:public Base{
 //everything Base contains and some more
}

I have a function in which I want to duplicate an array of these and then alter it.

void doStuff(Base *data, unsigned int numItems){
 Base *newdata = new Base[numItems];
 memcpy(newdata, data, numItems*sizeof(Base));
 ...
 delete [] newdata;
}

But if I used this function like so:

Base *data = new Derived[100];
doStuff(data, 100);

It wouldn't work, would it? Because Derived1 is larger than Base, so allocating for Base is not enough memory?

Was it helpful?

Solution

You could do this easily with a template:

template< class T >void doStuff(T *data, unsigned int numItems)
{
    T *newdata = new T[numItems];
    memcpy( newdata, data, sizeof( T ) * numItems );
    ...
    delete [] newdata;
}

Edit as per the comments: If you wanted to do this for a mixed collection things will get more complicated quickly ... one possible solution is this:

struct Base{
    virtual Base* CopyTo()      { return new Base( *this ); }
};

struct Derived:public Base{
    virtual Derived* CopyTo()   { return new Derived( *this ); }

};

void doStuff( Base** ppArray, int numItems )
{
    Base** ppNewArray   = new Base*[numItems];
    int count = 0;
    while( count < numItems )
    {
        ppNewArray[count] = ppArray[count]->CopyTo();
        count++;
    }

    // do stuff

    count = 0;
    while( count < numItems )
    {
        delete ppNewArray[count];
        count++;
    }
    delete[] ppNewArray;
}

OTHER TIPS

Exactly. This is a variation of the slicing problem.

You'll need to use pointers and use copy constructors. Oh and also, don't use the keyword struct for more than basic data structures. Technically it works, but what you're creating is class hierarchy, so use the class keyword.

This won't work simply because Derived is bigger and also is for intents and purposes a completely different object that is compatible with Base mainly by interface, but more importantly, when dealing with classes, you shouldn't really use low-level memory manipulation. Instead, you should be setting up copy constructors and use libraries like < algorithm > to perform templated actions on them.

Further more, the reason why it won't work, despite being legal syntax (i.e. Base * = Derived *), is that your allocating larger objects than what a Base * would index into, which would lead to memory corruption by writing memory to the wrong location.

For example, if a Base object is 4 bytes, C++ would index the array every four bytes, but if the actual allocated Derived objects are 8 bytes then you're indexing halfway across object boundaries and your member variables won't be pointing to the right location in memory.

Using class hierarchies in an array:

Base *objects[100];
for (int i = 0; i < 100; i++)
    objects[i] = new Derived();

Even further, to make things easier to manage, you may want to use a smart pointer mechanism and a template list instead of raw pointers.

YES! You're right. It wouldn't work. Because Derived1 is larger than Base, so allocating for Base is not enough memory.

Yes. The memory footprint of Derived is larger than the memory footprint for Base so the copy will not work as intended.

Well, an array of Derived is not an array of Base.

If you need to upcast a Derived* to a Base*, you should allocate an array of pointers to Base or, preferably, a vector<Base*>

vector<Base*> data(100);
// Initialize the elements
for (vector<Base*>::iterator it = data.begin(); it != data.end(); ++it)
{
    *it = new Derived;
}

doStuff(data);

// Destroy the elements
for (vector<Base*>::iterator it = data.begin(); it != data.end(); ++it)
{
    delete *it;
}

And your doStuff function becomes:

void doStuff(const vector<Base*>& data)
{
    // Copy the objects, not the pointers
    vector<Base*> newdata;
    for (vector<Base*>::const_iterator it = data.begin();
         it != data.end(); ++it)
    {
        newdata.push_back((*it)->clone());
    }

    // Do stuff

    // Destroy the copies
    for (vector<Base*>::iterator it = newdata.begin();
         it != newdata.end(); ++it)
    {
        delete *it;
    }
}

Note that, to copy the objects without knowing whether they are Base or Derived, we need to use the virtual constructor idiom. It requires modifying Base and Derived like this:

struct Base{
    ...
    virtual Base* clone() const { return new Base(*this); }
    virtual ~Base() {}
};

struct Derived : public Base {
    ...
    Derived* clone() const { return new Derived(*this); }
};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top