2012-01-05 72 views
-3

瞭解std::set.insert & std::vector行爲。 請考慮以下情形:按值傳遞,沒有與std :: vector賦值的深層副本?

A.H

class A { 
    uint id; 
    vector<double> values; 
    operator<(const A& argA) const; 
} 

A.cpp

A::A(uint argId, vector<double> argValues) { 
    this->id = argId; 
    this->values = argValues; 
} 

A::operator<(const A& argA) const { 
    // it's guaranteed that there's always at least one element in the vector 
    return this->values[0] < argA.values[0]; 
} 

B.cpp

std::set<A> mySet; 
for (uint i = 0; i < (uint) 10; i++) 
{ 
    vector<double> tempVector(3); 
    for (uint j = 0; j < (uint) 3; j++) { 
    tempVector[j] = j; 
    } 

    myset.insert(A(i + 1, tempVector)); 
} 

在我的理解中,tempElement擁有一個深度複製的向量(值),因爲vector在構造函數中按值傳遞並被賦值。因此,循環我不應該打破我的設置添加元素。但插入*tempElement休息 - SIGSEV。在我的邏輯這應該工作......每一個幫助表示讚賞!

編輯:代碼在插入過程中(第二個元素)崩潰; set調用LT-運算符,嘗試訪問傳入參數的向量 - 但不能。在創建A之前,我通過id和向量,檢查傳遞的向量是否包含正確的元素。

+4

爲什麼動態分配tempElement?您的代碼中存在內存泄漏(tempElement未被刪除)。你可以像這樣在一行中完成:myset.insert(A(i + 1,tempVector)); – 2012-01-05 18:56:41

+0

刪除myset時會被刪除。它調用所有包含元素的析構函數。 – Eric 2012-01-05 18:58:00

+1

什麼是tempComponents? – Zac 2012-01-05 18:58:51

回答

1

對於一個小矢量它應該沒關係,但是如果你有一個大的數組並且繼續複製它將會很昂貴,你的A應該包含某種類型的淺拷貝指針。有幾個選項:

  1. boost::shared_array<double>
  2. boost::shared_ptr<vector<double> >
  3. boost::shared_ptr<double>但陣列缺失者通過在建設中。
  4. 使一個不可複製的,並有一套(共享)指針與A與一些比較函數,比較指針而不是指針本身。

注意,無論使用哪種shared_arrayshared_ptr您將無法提取大小(元素的數量),所以你必須存儲分開。

+0

自從C++ 11開始,'std :: shared_ptr <>'也可用。 – 2012-01-05 19:02:33

+2

@WTP C++ 11還支持移動語義,其中(如果代碼被正確地重寫)應該消除所有額外的副本。 – 2012-01-05 19:13:27

0

我不認爲這個問題是在這個代碼中。不過,我注意到你有一個矢量tempVector,但你將值分配給tempComponents。我看不到tempComponents聲明,但我的猜測是它的大小不同。

+0

'tempComponents'已從問題中刪除。出現一個複製粘貼錯誤。 – 2012-01-05 20:15:50

0

不,沒有理由在這裏插入myset導致崩潰。問題必須存在於其他地方。也許在A的副本中,如果你不使用默認的。

但是,您的代碼泄漏內存。當你插入集合*tempElement被複制到集合中,然後你分配的原始文件new不再使用,但從不刪除。相反,您可以只做A tempElement(i+1,tempVector);,以便在將對象複製到集合中後,它會被正確銷燬。或者在這種情況下可能更好,你可以將它構造成臨時直接插入的myset.insert(A(i+1,tempVector)),在這種情況下,對象將被移動而不是被複制,從而減少了開銷。或者你可以只是建立對象以避免移動:myset.emplace(i+1,tempVector);

另外我假設你的意思是tempComponents[j] = j;你的意思是tempVector[j] = j。您可以用std::iota(begin(tempVector),end(tempVector),0)替換該循環。 編輯:或者你可以使用新的初始化語法此外,由於矢量是相同的,每次你可以只使用一個外循環:

vector<double> tempVector(3) = {0.0,1.0,2.0} 
std::set<A> mySet; 
for (uint i = 0; i < (uint) 10; i++) 
{ 
    myset.emplace(i+1,tempVector); 
} 

C++編譯器03將不支持emplace或新初始化器語法和iota將是它們的編譯器擴展(它來自原始的SGI STL,所以有些可能有它)。對於那些你仍然會使用插入和使用一個for循環初始化tempVector或使用數組:

double tempVector_init[] = {0.0,1.0,2.0}; 
vector<double> tempVector(tempVector_init,tempVector_init+3); 
std::set<A> mySet; 
for (uint i = 0; i < (uint) 10; i++) 
{ 
    myset.insert(A(i+1,tempVector)); 
} 
+0

謝謝@ bames54,但我使用的是不支持此語法的舊版本。 – Eric 2012-01-06 09:16:41

+0

@Eric如果'emplace'不被支持,你仍然可以'插入(A(i + 1,tempVector)',並且只要沒有進一步修改代碼就可以獲得移動語義的好處。將爲C++ 03編譯器添加我的示例代碼版本 – bames53 2012-01-06 13:08:45

0

工作代碼許多變化 - 但我沒有看到你描述的問題。

#include <set> 
#include <vector> 

using namespace std; 

typedef unsigned int uint; 

class A { 
public: 
    A(uint argId, vector<double> argValues) 
    { 
    this->id = argId; 
    this->values = argValues; 
    } 

    bool operator < (A const& a) const 
    { 
    return a.id < id; 
    } 

    uint id; 
    vector<double> values; 
}; 


int _tmain(int argc, _TCHAR* argv[]) 
{ 

    std::set<A> mySet; 
    for (uint i = 0; i < (uint) 10; i++) 
    { 
    vector<double> tempVector(3); 
    for (uint j = 0; j < (uint) 3; j++) { 
     tempVector[j] = j; 
    } 

    std::unique_ptr<A> tempElement(new A(i + 1, tempVector)); 
    mySet.insert(*tempElement); 
    } 

    return 0; 
} 
+0

感謝您的努力@Zac。我正在使用另一個不支持std :: unique_ptr語法的版本。 – Eric 2012-01-06 09:19:11