我的实现使用std :: map存储数据。当我启动代码时,这似乎是最好的选择。现在,我不得不更改地图中所有对象的键值。
问题在于每个对象都指向地图内的另一个对象:
class AND : public node{
vector <node*> inputs;
vector <node*> outputs;
}
地图的声明如下:
map<unsigned int, AND> all_ANDs;
我的问题是:如果我使用C ++ 17中的map :: extract修改all_ANDs映射中的键值,我的指针(例如,属性输入中的指针)将继续指向合适的地方?
换句话说::如果我用extract更改“ first”元素的值,那么“ second”的地址会保持不变吗?
我从this link中注意到,字符串“木瓜”保持不变(并且正常运行)。但是我想确定指针。
答案 0 :(得分:5)
是
您已经在帖子中引用的reference明确指出没有元素被复制或移动。 (这假设您的代码段中的node
没有引用map::node_type
)。
对映射节点的insert
操作 (修改其键之后)也是如此:
如果插入成功,则将其保留在节点句柄中时获得的指向该元素的指针和引用无效,并且在提取该元素之前获得的指向该元素的指针和引用变为有效。 (自C ++ 17起)
但是,访问extract()
和re insert()
之间的对象具有未定义的行为,并且在提取状态下获取其地址的用途受到限制。引用标准:
提取成员仅使已删除元素的迭代器无效; 指向删除元素的指针和引用仍然有效。然而, 通过这样的指针和引用访问元素,而 元素归node_type拥有是未定义的行为。参考和 指向由node_type拥有时获得的元素的指针是 如果成功插入元素,则无效。
说明
本质上,map<>
被实现为节点树,每个节点都包含一个key
和T
(向用户公开为pair<const Key, T>
)。节点(通常)在堆上分配,并且对象的地址与节点的地址相关。 map::extract()
取消节点与树的链接并返回节点句柄(持有指向地图节点的指针的对象),但是AFAIK节点本身并未重新分配,移动,或复制。在map::insert(handle)
上,该节点根据其(新)键重新链接到树中。同样,这不涉及节点的重新分配,移动或复制。
备注
上面是一个粗略的草图。实际操作方式可能更复杂,而且实现也有所定义。如here所述,node_handle
确实允许通过成员函数更改键
Key &node_handle::key() const;
没有具体说明如何实现此目的,我推测该实现使用联合或某种强制转换来允许此操作。当然,该地图必须向用户显示pair<const Key,T>
,以防止用户更改密钥并因此破坏地图,但这与从地图中提取的元素无关。
答案 1 :(得分:1)
我上面的回答解决了您的直接问题。但是,正如我在评论中建议的那样,它似乎是XY problem。我怀疑:
您具有AND
对象的某种结构,这些对象通过它们的inputs
和outputs
字段相互链接。此链接不得因任何重新分配而中断,因此您不能将它们存储在不断增长的vector<AND>
中并进行重新分配。
您还希望根据某些key
对这些对象进行排序,并因此将它们存储在map<Key,AND>
中,该对象实际上在增长时不会重新分配。
您现在要根据另一个键(和/或更改所有键)对其进行排序。
(如果您实际上对排序不感兴趣,而仅是通过它们的键来查找对象,则应该使用unordered_map
代替map
,find()
在{ {1}}而不是O(n)
个操作。)
我建议您使用其他数据布局:
您存储O(log(n))
对象的方式允许增加它们的数量而无需重新分配。这里一个明显的选择是deque<AND>
,因为
在双端队列两端的插入和删除操作永远不会使之无效 指向其余元素的指针或引用
您还可以将AND
设为不可复制且不可移动,以确保一旦分配它们的地址就不会更改(指向它们的指针在销毁之前一直保持有效)。
通过对AND
的向量进行排序或使用{{1}对存储对象的指针进行实际处理,可以支持任何按键查找或按键排序操作}}或pair<key,AND*>
。您甚至可以同时为每个对象拥有各种键(每个对象都有一个映射)。
当您必须重新设置所有对象的键时,只需忘记旧的映射并创建一个新的映射:由于该映射仅存储指针而不存储对象,因此这不会影响您的链接。
答案 2 :(得分:0)
您的map
保存实际的AND
对象,而不是指向对象的指针。因此,如果存储在AND*
中的vector
指针指向map
的{{1}}对象,则一旦这些对象为,这些指针将变为无效。从AND
中删除。
但是,提取只是从map
断开指定节点的链接,因此该节点及其键和值在内存中仍然有效。可以将节点重新插入到map
中,而不会影响节点的键和值的地址。在这方面,map
中的指针将不会变为无效(尽管在将节点从容器中分离时,取消定义它们的引用是未定义的)。
另一种选择是更改您的vector
来容纳map
指针。或者,最好考虑在AND*
中使用std::shared_ptr<AND>
中的map
和std::shared_ptr<node>
中的vector
,而不是原始指针。那么map
项是删除还是提取都没关系,只要活动{ {1}}对它们的引用。