std::map,指向 map 键值的指针,这是否可行?

33
std::map<std::string, std::string> myMap;

std::map<std::string, std::string>::iterator i = m_myMap.find(some_key_string);
if(i == m_imagesMap.end())
    return NULL;

string *p = &i->first;

最后一行有效吗? 我想把这个指针p存到别的地方,它在整个程序的生命周期内都有效吗? 但如果我向该映射添加更多元素(使用其他唯一键)或删除其他键,那么它不会重新分配此字符串(键-值对),因此p会变得无效吗?


2
简短回答:可能不行。长回答:除非你之前使用了 typedef const std::string string; 而不是 using namespace std;,否则它无法编译,这很令人困惑... - pqnet
4个回答

60

第23.1.2节#8(关联式容器需求):

插入成员不应影响容器的迭代器和引用的有效性,而擦除成员仅应使迭代器和引用无效。

所以说,存储指向map元素数据成员的指针是被保证有效的,除非您删除了该元素。


2
实际上,不完全是这样.../迭代器/是稳定的,但是保持迭代器稳定并不意味着保持指针稳定(您可以想象垃圾回收器确保每个迭代器跟踪数据,但不是指针)。 - PierreBdR
5
是的,Greg,我们是正确的。23.1.2/8中写道:“插入成员不会影响迭代器和容器引用的有效性,而删除成员只会使指向被删除元素的迭代器和引用失效。”当然,我给你加一分。 - Johannes Schaub - litb
谢谢提供参考。我没有实际标准的副本,网上的草案标准也没有这个内容。C++0x草案标准在23.1.4#8中有此内容。 - Greg Rogers
1
标准措辞有点奇怪,谈论“对容器的引用”... 此外,对于C++11和无序容器,意图类似,措辞更清晰:插入和emplace成员不应影响对容器元素的引用的有效性,但可能使容器的所有迭代器失效。erase成员只会使迭代器和对已删除元素的引用失效。 - PlasmaHH
3
请注意:该文本已经移动到新的章节p.744,2013年10月13日的新版本标准草案中的第23.2.4#9项。[N3797, 2013-10-13](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3797.pdf) - eonil
显示剩余3条评论

21

首先,映射(maps)保证具有稳定性; 即,迭代器不会因元素插入或删除而失效(当然除了被删除的元素)。

然而,迭代器的稳定性并不能保证指针的稳定性!虽然大多数实现通常使用指针来实现迭代器(这意味着假设您的解决方案将很安全),但并不总是如此。您应该真正存储的是迭代器本身。

您可以创建一个类似于以下的小对象:

struct StringPtrInMap
{
  typedef std::map<string,string>::iterator iterator;
  StringPtrInMap(iterator i) : it(i) {}
  const string& operator*() const { return it->first; }
  const string* operator->() const { return &it->first; }
  iterator it;
}

然后将其存储而不是一个字符串指针。


谢谢,但我需要将指针作为Windows消息LPARAM传递,我可以将迭代器转换为LPARAM,然后再将其转换回迭代器吗? - michael
不行,但是你可以存储迭代器,然后使用 &i->first,它是你需要的字符串*。正如Pierre所说,这应该是最便携和安全的解决方案。 - Gorpik
10
嗯...只是看了一下其他得到高赞的回答。由于标准(同样)要求引用是稳定的,这意味着指针也需要是稳定的,不仅仅是迭代器。 - Martin Ba

1

如果您不确定哪些操作会使您的迭代器无效,您可以在参考文献中轻松查找。例如,对于vector::insert,它说:

这实际上增加了向量大小,如果且仅当新向量大小超过当前向量容量时,会自动重新分配已分配的存储空间。向量容器中的重新分配将使所有先前获得的迭代器、引用和指针无效。

另一方面,map::insert没有提到任何类似的内容。

正如Pierre所说,您应该存储迭代器而不是指针。


2
cplusplus.com不是引用那种东西的好来源。总的来说,cplusplus.com的例子都很差。我不是因为好玩才这么说的,而是我知道 - 我已经看过很多次了。它质量差的最好例子是http://www.cplusplus.com/reference/iostream/ostream/operator%3C%3C.html。 - Johannes Schaub - litb
将以下有关编程的内容从英语翻译成中文。仅返回翻译后的文本:无论您的答案如何,我只是告诉您我对该网站的看法。 - Johannes Schaub - litb
如果你手头没有书,那么你会用什么作为参考呢?www.cppreference.com 仍然感觉不完整。 - drby
hackingwords。我经常在cplusplus.com上查找函数名称的快速参考。对于“stl”,我会查看SGI网站,我认为它非常好。他们没有记录C++流和其他不在SGI stl中的内容,因此我会在cplusplus.com上查找这些内容。 - Johannes Schaub - litb
参考链接:http://www.sgi.com/tech/stl/。祝玩得愉快。wg21在这里:http://www.open-std.org/jtc1/sc22/wg21/。最新的工作文件(虽然是C++1x)在这里:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2800.pdf。当然,你也可以找到C++98的草案。 - Johannes Schaub - litb
显示剩余2条评论

0

你为什么想要这样做?

你不能改变*p的值,因为它是const std::string。如果你改变了它,那么你可能会破坏容器的不变量,从而改变元素的排序顺序。

除非你有其他要求没有在这里给出,否则你应该只复制字符串。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接