2011-04-16 49 views
1

這是靈感來自Effective C#第一版中的一個項目,警告關於凌駕GetHashCode()當一個`key/value`插入到`std :: map`中時,它是否創建了它自己的對象副本?

對不起,我沒有支持的代碼。順便說一下,這不是一項家庭作業,我只是不熟悉C++/STL,並且找不到有關實施的信息。

想我創造我自己的類被點名的人擁有3個公共可變的字符串字段:

  • 名字,
  • 中間初始

它還提供了一個更小比操作人員先把姓名與先姓,然後是中間姓名,然後是姓氏進行比較 - 這就是全部。

我創建一個從人到int(比如年齡)的地圖,並用大約20個鍵/值對填充它。我還將指針存儲在數組中。然後我改變第五個指針指向的對象的名字,然後嘗試使用這個修改過的鍵來查找對應的年齡(記住對象是可變的並且全開)。

爲什麼會發生這種情況?

A)因爲std::map使用的密鑰沒有改變(被複制),我改變了我自己的副本,現在我的密鑰沒有找到。但這怎麼可能呢?我沒有提供我自己的拷貝構造函數。編譯器可能創建了一個默認的?

B)std::map集合實際上是一棵紅黑樹,我碰巧有一個直接指向某個鍵的指針。當我更改密鑰時,我直接在樹的節點中更改它。現在很可能是我的節點放置不正確,並且不會使用適當的樹搜索算法找到它。我應該刪除節點,然後修改它們的關鍵字,然後重新插入它。如果是這樣的話,那麼我懷疑STL集合通常是相當危險的,會導致noobs犯很多錯誤。

C)還有別的嗎?

我將不勝感激您的見解。

+0

你在密鑰中使用了什麼特定的數據類型?你說的字符串,但要清楚,他們是std ::字符串?如果是這樣,A是正確的 - STL容器將複製鍵和值中的所有數據。 – Joe 2011-04-16 15:12:21

回答

5

當你使用std容器時,所有的數據都被複制到容器中。對於地圖來說,這沒有什麼不同

映射數據位置的一個限制是密鑰不可變。一旦它被插入,它被固定以改變你必須找到/擦除的鍵並重新插入以改變鍵的值。

struct Person 
{ 
    std::string first; 
    std::string middle; 
    std::string last; 
    Person(std::string const& f, std::string const& s, std::string const& l) { BLABLA } 
    bool operator<(Person const& rhs)          { return BLABLABLA;} 
}; 
std::map<Person,int> ageMap; 

ageMap[Person("Tom", "Jones", "Smith")] = 68; 
ageMap[Person("Tom", "I",  "Smith")] = 46; 
ageMap[Person("Tom", "II", "Smith")] = 24; 

當你創建你的Person數組時,它將失敗,除非該數組包含const指針。

Person* pMap[3]; 
pMap[0] = &ageMap.begin().first;  // Fail need a const pointer. 

Person const* pMapConst[3]; 
pMapConst[0] = &ageMap.begin().first; // OK. Note a const pointer. 
1

這些條目總是進行復制。如果密鑰類型是std::string,那麼,是的,這是一個副本。 (在幕後,std :: string做了一些優化,所以角色不一定總是被複制,但除此之外)。

(我想沒有辦法讓指針進入地圖的對象,所以你如果你的密鑰類型是*std::string(一個指針!),那麼指針中的位被複制,但是如果這個值不是特定字符串實例的內容稍後會更改,那麼密鑰將會有效更改。

(和比較必須是適合您的密鑰類型。)

4

標準容器具有該類存儲在其中具有價值的語義,因此它們被複制的要求。但是

  • 如果你存儲了任何類型的指針,顯然指向的內容不被複制;
  • 如果您嘗試修改存儲的密鑰(您可以獲得對其的引用),特別是使用可變成員或const_cast玩弄技巧以便能夠執行此操作,但排序順序並不守恆,你在UB領域。
+0

什麼是UB領域?或者它在哪裏? – idichekop 2017-06-08 21:10:07

1

是的 - 當你插入一個項目到一個std :: map,你傳遞它的值,所以它包含的是你傳遞的副本。是的,除非你自己聲明一個,否則編譯器會爲你合成一個拷貝構造函數。

可以創建(例如)使用指針作爲其鍵的映射(以及比較指針指向的比較函數/函子)。但是,如果您嘗試修改這些指針指向的鍵,則會得到UB。如果要修改set/map/multiset/multimap中的密鑰,則需要從集合中刪除現有項目,修改您的副本,然後將修改後的版本插入到集合中。

相關問題