这是节选:
...
std::vector<std::wstring> vecWstr;
vecWstr.emplace_back(L"1");
wchar_t* data1 = vecWstr[0].data(); //<-This pointer needed for future use.
vecWstr.emplace_back(L"2");
wchar_t* data2 = vecWstr[0].data();
if (data1 != data2)
MessageBox(L"Error, not equal.", L"Compare");
MessageBox
经常出现。
因此,这里我比较wstring
之前和之后的两个.emplace()
缓冲区。以我的理解,它们必须相等。
这里主要关注的是:为什么vector
在设置第二个内部std::wstring
元素后移动/重新分配了它?
这个问题是在经过奇怪的程序行为后进行调查后产生的。
如果我在第二次vecWstr[0].data()
之前之前保存.emplace()
缓冲区指针,则缓冲区指针将变得过时并且程序的行为不当。
最大的问题是程序中有很多std::vector<std::wstring>
,但它们似乎都按预期运行,到目前为止,只有一个如上所示。
这一切都在 MSVS 16.1.5
问题是:
谁在这里? std::vector
是否可以更改/移动其std::wstring
元素的内部缓冲区?
答案 0 :(得分:7)
在C ++ STL中,有一种称为指针无效的东西。这意味着,当您获取容器中某个元素的指针,然后又修改了该容器时,在修改之后,您的指针可能不再有效。
指针无效的规则由标准定义,在容器与容器之间,操作与操作之间有所不同。
在您的情况下,您有一个std::vector
。如果您emplace_back
,并且vector需要更大的容量来添加元素,则对vector元素的引用/指针/迭代器将不再有效。在这种情况下,向量会在内存中分配另一个更大的空间,并将所有元素移到那里。
但是等等!
您正在直接从字符串中获取data()
指针!为什么此指针也无效? wstring
不应该是仅包含指向某些堆缓冲区的指针的轻量级结构吗?
好吧,这是SSO(小字符串优化)的魔力。如果您的字符串足够小,则wstring
仅将其缓冲区存储在数据结构本身中(而不是存储指向缓冲区的指针)。在这种情况下,当您移动它时,指针当然会失效。
您的字符串很小(1个宽字符),因此它满足SSO的条件。如果您使用更长的版本:
std::vector<std::wstring> vecWstr;
vecWstr.emplace_back(L"asdfghjkl");
wchar_t* data1 = vecWstr[0].data(); //<-This pointer needed for future use.
vecWstr.emplace_back(L"qwertyuiop");
wchar_t* data2 = vecWstr[0].data();
if (data1 != data2)
MessageBox(0, L"Error, not equal.", L"Compare", 0);
return 0;
消息框可能不会弹出。
但是,您无法控制运行时字符串的长度,并且您也不知道编译器将如何实现SSO,因此请不要这样编码! >
相反,可以使用reserve
方法(如songyuanyao建议),或使用其他在添加元素时不会使指针无效的容器。请参阅std::list和std::deque。阅读有关其指针/引用/迭代器无效的部分。