2017-08-18 45 views
0
class Example 
{ 
    public: int i; 
    Example(const Example &e) 
    { 
     i = e.i; 
    } 
    Example(int i) 
    { 
     this->i = i; 
    } 
}; 

int main() 
{ 
std::vector<Example*> vec; 
std::vector<Example*> newVec; 

Example* ex1 = new Example(1); 
Example* ex2 = new Example(2); 

vec.push_back(ex1); 
vec.push_back(ex2); 

//newVec = vec; --> This does shallow copy 

for(int i=0; i<vec.size(); ++i) // --> Deep copy 
{ 
    Example newE(*(vec[i])); 
    newVec.push_back(&newE); 
} 

for(int i=0; i<newVec.size(); ++i) 
{ 
    std::cout << "\nfoobar" << newVec[i]->i << "\n"; 
} 
} 

上面的代碼打印兩次foobar2。它不應該打印foobar1和foobar2?另外,這是複製包含對象的矢量的最佳方法嗎?我想深入複製。向量中的push_back是否在同一位置插入?

+7

'newVec.push_back(&newE);'將指針推到一個局部變量,立即超出範圍使其成爲一個懸掛指針 – VTT

+0

...所以從那一刻起,任何事情都可能發生,訪問該指針可以返回隨機的東西, – spectras

+1

你的向量不包含對象,它們包含指針,使用帶有對象的向量以獲得更舒適的生活 – molbdnilo

回答

5
for(int i=0; i<vec.size(); ++i) // --> Deep copy 
{ 
    Example newE(*(vec[i])); 
    newVec.push_back(&newE); 
} 

在這段代碼中你做的vec[i]副本Example newE。然後你push_back地址newEnewVec載體。然後newE對象超出範圍並被銷燬,所以最終有一個指向newVec內部垃圾的指針。

如果你想的矢量內容的深層副本,要存儲擁有對象的指針,可以考慮使用智能指針,例如的矢量vector<shared_ptr<Example>>

在這種情況下,你可以簡單的用向量複製operator=shared_ptr S的引用計數將自動更新是。

Yoy可能還想考慮只有vector<Example>(無指針間接尋址)的簡單設計。

+0

謝謝。讓我們說我不能改變已經實現的東西。使用轉換函數創建指向深拷貝對象的向量是我在這裏閱讀的一種方法:https://stackoverflow.com/questions/16475042/copy-from-vectorpointer-to-vectorpointer-in-c。 這是一個壞方法嗎? –

2
Example newE(*(vec[i])); 
newVec.push_back(&newE); 

在棧上分配newE,然後將一個指針指向它到vector中。 newE在循環迭代結束時超出範圍,並且您只是很幸運newE的位置未被重用。看起來編譯器生成的代碼將兩個堆棧實例放在同一個地方,因此您用第二個值覆蓋該位置,然後在遍歷指針的向量時,重新引用該位置兩次。

3

您的代碼不正確,導致未定義的行爲。

這個循環是有問題的:

for(int i=0; i<vec.size(); ++i) // --> Deep copy 
{ 
    Example newE(*(vec[i])); 
    newVec.push_back(&newE); 
} 

您聲明一個局部變量newE。這種變量具有自動存儲時間(通常稱爲堆棧分配,儘管C++標準在技術上不需要堆棧來實現它們)。

變量具有自動存儲時間具有以下屬性:在聲明它們的點

  • 其壽命開始。
  • 在範圍的結束其壽命結束時,他們在聲明。

for循環的每次迭代是一個範圍,所以在第一次迭代後,newE已不再有效,編譯器自由地生成將重用內存的指令。

這正是您的情況(以及任何體面的實施)發生的情況:每次迭代都會將newE置於完全相同的地址。

但是,正如你所用operator&採取newE地址,然後newE超出範圍,你現在有一個懸擺指針

搖晃指針本身並不存在問題,但您無法對它們做任何有意義的事情。特別是,取消引用一個導致未定義的行爲


您有多種方法來修復您的代碼。

最簡單的方式將使用new

Example* newE = new Example(*vec[i]); 
newVec.push_back(newE); 

但你必須確保添加佔有delete了。此外,這不是真正迂腐的C++。

另一種方法是將vecnewVec更改爲std::vector<Example>,以迴避原始指針問題。這將是用C++表達你的代碼的首選方式。

在某些情況下,你需要指針,例如,如果你做多態。然後,您可以使用std::unique_ptrstd::shared_ptr具有指針語義和RAII。