C ++中的内存分配和继承类

时间:2010-02-18 22:16:45

标签: c++ inheritance dynamic-memory-allocation

说我有这些结构:

struct Base{
 ...
}

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

我有一个函数,我希望复制这些函数的数组,然后改变它。

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

但如果我像这样使用这个功能:

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

它不会起作用,不是吗?因为Derived1比Base大,所以为Base分配内存不够吗?

6 个答案:

答案 0 :(得分:4)

完全。这是slicing problem的变体。

答案 1 :(得分:2)

您需要使用指针并使用复制构造函数。哦,而且,不要将关键字struct用于基本数据结构。从技术上讲,它可以工作,但您创建的是类层次结构,因此请使用class关键字。

这不会简单,因为Derived更大,并且对于意图和目的来说是一个完全不同的对象,主要通过接口与Base兼容,但更重要的是,在处理类时,你不应该真的使用低级内存操作。相反,您应该设置复制构造函数并使用像<算法>对他们进行模板化的行动。

更多,尽管是合法的语法(即Base * = Derived *),但它无法工作的原因是你分配的大对象比Base *更大index into,通过将内存写入错误的位置会导致内存损坏。

例如,如果Base对象是4个字节,C ++将每四个字节索引一次数组,但如果实际分配的Derived个对象是8个字节,那么您将跨越对象边界进行索引并且您的成员变量不会指向内存中的正确位置。

在数组中使用类层次结构:

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

更进一步,为了使事情更易于管理,您可能希望使用智能指针机制和模板列表而不是原始指针。

答案 2 :(得分:1)

YES!你是对的。它不会起作用。因为Derived1比Base大,所以为Base分配的内存不够。

答案 3 :(得分:0)

您可以使用模板轻松完成此操作:

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

根据评论进行编辑:如果你想为混合收藏品做这件事,事情会变得更加复杂......一个可能的解决方案就是:

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;
}

答案 4 :(得分:0)

是。 Derived的内存占用量大于Base的内存占用量,因此副本无法按预期工作。

答案 5 :(得分:0)

好吧,an array of Derived is not an array of Base

如果您需要将Derived*转发到Base*,则应该分配一个指向Base的指针数组,或者最好是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;
}

您的doStuff功能变为:

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;
    }
}

请注意,要复制对象而不知道它们是Base还是Derived,我们需要使用virtual constructor idiom。它需要像这样修改BaseDerived

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

struct Derived : public Base {
    ...
    Derived* clone() const { return new Derived(*this); }
};