这是受到有效C#第一版中的项目的启发,警告天真地覆盖GetHashCode()
。
抱歉,我没有支持代码。顺便说一句,这不是一个功课,我对C++/STL
并不熟悉,也无法找到有关实施的信息。
假设我创建了一个名为person的类,它有3个公共可变字符串字段:
它还提供了一个小于运算符,根据名字首先比较一个人,然后是中间名,然后是姓氏 - 就是全部。
我创建了一个从人到int(比如年龄)的地图,并用大约20个键/值对填充它。我还存储指向数组中键的指针。然后我更改了第五个指针所指向的对象的第一个名称,并尝试使用此修改后的键查找相应的年龄(请记住该对象是可变的并且是大开的)。
为什么会这样?
A)因为std::map
使用的密钥没有改变(被复制),我改变了自己的副本,现在找不到我的密钥。但这怎么可能呢?我没有提供自己的复制构造函数。也许默认的是由编译器创建的?
B)std::map
集合实际上是一个红黑树,我碰巧有一个指向密钥的直接指针。当我更改了密钥后,我直接在树的节点中更改了密钥。现在很可能我的节点没有正确定位,并且使用正确的树搜索算法找不到。我应该删除该节点,然后修改它们的密钥,然后再次重新插入它。如果是这种情况,那么我怀疑STL
集合通常是相当危险的,并且会导致新手犯错误。
C)还有别的吗?
感谢您的见解。
答案 0 :(得分:8)
使用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 :(得分:7)
标准容器要求存储在其中的类具有值语义,因此它们被复制。但
答案 2 :(得分:2)
条目总是复制。如果密钥类型是std::string
,那么,是的,这是一个副本。 (在幕后,std :: string会进行一些优化,因此不一定总是复制字符,但除了这一点之外。)
(我认为无法获得指向地图对象的指针,因此您无法再次更改该密钥,只需在迭代或其他检索时获取副本。)
现在,如果您的密钥类型是*std::string
(一个指针!),那么指针中的位将被复制,但如果稍后更改了特定字符串实例的值,则该密钥将被有效地更改。
(比较器需要适合您的密钥类型。)
答案 3 :(得分:1)
是 - 当您将项目插入std :: map时,您可以按值传递它,因此它包含的是您传递的内容的副本。是的,编译器会为你合成一个复制构造函数,除非你自己声明一个。
可以创建(例如)使用指针作为其键的映射(以及比较指针引用的比较函数/函数)。但是,如果您尝试修改这些指针指向的键,则会获得UB。如果要修改set / map / multiset / multimap中的键,则需要从集合中删除现有项目,修改副本,然后将修改后的版本插回到集合中。