2010-06-06 29 views
6

我發現了一個關於stl地圖的非常偏見的事實。出於某種原因,我不能讓對象被插入到地圖中,只能構造/析構一次。如何獲取stl地圖構造/銷燬插入的對象只有一次

實施例:

struct MyObject{ 
    MyObject(){ 
     cout << "constructor" << endl; 
    } 
    ~MyObject(){ 
     cout << "destructor" << endl; 
    } 
}; 
int main() { 
    std::map<int, MyObject> myObjectsMap; 
    myObjectsMap[0] = MyObject(); 
    return 0; 
} 

回報:

constructor 
destructor 
destructor 
constructor 
destructor 

如果我:

typedef std::pair<int, MyObject> MyObjectPair; 
myObjectsMap.insert(MyObjectPair(0,MyObject())); 

回報:

constructor 
destructor 
destructor 
destructor 

我插入的對象負責他們自己的內存分配,所以當被破壞時,他們會清理自己,多次被破壞正在造成一些麻煩。

+1

顯然,'std :: map '不提供用於創建新條目的非複製接口。我建議在這裏共享指針 - 'std :: map >' – 2010-11-14 11:40:40

回答

1

就是這樣map和其他容器的工作,你無法繞過它。例如,這就是爲什麼std::auto_ptr不能在集合中使用的原因。

+0

我想我會切換到一個向量並在地圖上保存指針。 – 2010-06-06 17:02:59

6

我建議你添加一個拷貝構造函數 - 這就是我認爲用於'缺少'構造的東西。

代碼:

#include <iostream> 
#include <map> 

using namespace std; 

struct MyObject{ 
    MyObject(){ 
     cout << "no-arg constructor" << endl; 
    } 
    MyObject(const MyObject&) { 
    cout << "const copy constructor" << endl; 
    } 
    ~MyObject(){ 
     cout << "destructor" << endl; 
    } 
}; 
int main() { 
    std::map<int, MyObject> myObjectsMap; 
    myObjectsMap[0] = MyObject(); 
    return 0; 
} 

輸出:

no-arg constructor 
const copy constructor 
const copy constructor 
destructor 
destructor 
no-arg constructor 
destructor 
destructor 
4

std::map允許使對象的多個副本,因爲它的願望。這是實現定義的,你無法控制這個。順便說一下,您注意到的「缺失」構造可能用於調用您未定義的複製構造函數。

你可以做什麼,但是使用輕量級,所以構造一個對象實際上是從一個預先存在的對象池中獲取一個已存在的對象,而破壞一個對象卻什麼也不做。池永遠不會釋放它的對象,但它始終保持對所有對象的處理。這樣,從開始到停止,您的內存使用量都很大,但是在整個程序生命週期中,它並沒有太大的變化。

2

爲了能夠在標準容器中使用,您的對象必須是可複製和可分配的。如果你的物體不符合這個你可能會有問題。

也就是說,如果(如您的示例代碼表示),你只需要插入地圖默認的構造的對象,你可以只使用操作符[]爲它的副作用:

// Insert default constructed MyObject at key 0 
myObjectsMap[0]; 

編輯

我不太清楚你的問題,但如果你不清楚構造對象的數量並且認爲存在構造函數/析構函數不匹配,那麼請注意,編譯器將提供一個不記錄到std::cout的拷貝構造函數因爲你不提供用戶聲明的。

+0

我希望能夠只有一個建築和一個破壞,就像我使用的是矢量一樣。矢量 v; v.push_back(爲MyObject()); //只產生一個構造和破壞。 – 2010-06-06 17:13:31

+0

您是否測試過'myObjectsMap [0];'實際上做了什麼? – 2010-06-06 17:15:32

+0

嗯,不是真的,我實際上使用了插入方法,它的開銷較少,但仍然會產生比我期待的更多的副本。無論哪種方式,我認爲我會留在矢量和地圖只用於指針。上面的帖子非常清楚最新的情況。很高興知道。 – 2010-06-06 17:37:22

2

當您說myObjectsMap [0]時,您正在調用MyObject的默認構造函數。這是因爲[0]中沒有任何內容,並且您只是訪問它。 It's in the manual。當你點擊MyObject()時,您正在使用默認構造函數創建臨時MyObject實例。

由於您允許編譯器定義您的拷貝構造函數,因此您會看到比構造函數消息更多的析構函數。 (與構建房屋不同,只能有一個析構函數,但是有很多構造函數,如果您的對象不應該被複制),那麼您可能需要聲明一個專用的複製構造函數和複製賦值運算符。

你調用默認的構造函數和拷貝構造函數兩次每個與此代碼:

myObjectsMap[0] = MyObject(); 

當你這樣做:

myObjectsMap.insert(MyObjectPair(0,MyObject())); 

調用默認的構造函數一次,複製構造函數3次。

你應該使用指針作爲映射值而不是對象本身,特別是我建議看看shared_ptr。

note: tests were done using GCC 3.4.5 on a Windows NT 5.1 machine.