2010-02-18 100 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); } 
};