2010-02-18 101 views
2

说我有这些结构:内存分配和用C继承类++

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

+0

这是行不通的,问题是究竟是什么ü要做到用C – Drakosha 2010-02-18 22:18:56

+1

阵列++有这些类型与继承陷阱的。这是我几乎总是使用'vector <>'的一个原因。虽然'Derived *'可以用作'Base *',但是数组不是多态的,但是形成了。 – 2010-02-18 22:39:08

+1

'memcpy'应该是'std :: copy',并使用'std :: vector'。 – GManNickG 2010-02-18 22:43:43

回答

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

这似乎是好的,但如果你想要这些东西的混合类型的集合不会工作。但是,也许OP没有做混合收藏? – 2010-02-18 22:23:21

+0

不,它不适用于混合收藏。根据OP的例子,你很难推动定义一个收集到的项目数组。你需要指针指针。此时,您需要使用某种形式的虚拟分配功能来执行正确的复制。 – Goz 2010-02-18 22:25:51

1

是的!你是对的。它不会工作。由于Derived1大于Base,因此为Base分配内存不足。

2

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

这不会工作,因为衍生更大,意图和目的是一个完全不同的对象,主要通过接口与Base兼容,但更重要的是,在处理类时,不应该使用低级存储器操作。相反,您应该设置复制构造函数并使用像<算法>的库来对它们执行模板化操作。

更进一步,为什么它不会工作,尽管是合法的语法(即Base * = Derived *)的原因,是你比分配较大的物体有什么Base *将索引到,这将通过写入内存导致内存破坏到错误的位置。例如,如果一个Base对象是4个字节,C++将每四个字节索引数组,但如果实际分配的对象是8个字节,那么您将在对象边界中间索引一半,并且您的成员变量不会指向内存中的正确位置。在一个阵列

使用类层次结构:

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

甚至进一步,使事情更容易管理,你可能想使用智能指针机制和模板列表,而不是原始指针。

+0

+1为正确的OOP方式来做到这一点。记住最初的'Base * data = new Derived [100];'也许是合法的语法,但是从多态的角度来看没有意义(数组并不总是与指针相同的东西)也是很重要的。 – JonM 2010-02-18 22:39:50

+0

@JonM你是对的。例如,当编译器将它们编入索引为4字节的对象时,它本质上会分配100个8字节的对象,这会导致内存损坏。第二个基地指数实际上是第一个的下半部分。 – 2010-02-18 22:46:08

0

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

0

那么,an array of Derived is not an array of Base

如果需要上溯造型一Derived*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); } 
};