2011-04-16 54 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中的密钥,则需要从集合中删除现有项目,修改您的副本,然后将修改后的版本插入到集合中。

相关问题