2

假设我有这些结构:

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 分配内存不够?

4

6 回答 6

4

确切地。这是切片问题的一种变体。

于 2010-02-18T22:19:24.117 回答
2

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

这不会仅仅因为 Derived 更大,并且出于意图和目的,这是一个完全不同的对象,Base主要通过接口兼容,但更重要的是,在处理类时,您不应该真正使用低级内存操作。相反,您应该设置复制构造函数并使用 < algorithm > 之类的库对它们执行模板化操作。

此外,尽管是合法的语法(即),但它不起作用的原因Base * = Derived *是您分配的对象比 aBase *将索引的对象更大,这会通过将内存写入错误的位置而导致内存损坏。

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

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

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

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

于 2010-02-18T22:30:40.537 回答
1

是的!你是对的。它行不通。因为 Derived1 比 Base 大,所以给 Base 分配内存是不够的。

于 2010-02-18T22:19:06.977 回答
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;
}
于 2010-02-18T22:21:24.600 回答
0

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

于 2010-02-18T22:35:30.680 回答
0

好吧,Derived 数组不是 Base 数组

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

请注意,要在不知道它们是Baseor的情况下复制对象Derived,我们需要使用虚拟构造函数 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); }
};
于 2010-02-18T22:36:52.457 回答