std :: vector< std :: wstring>移动/重新分配内部wstring.data()是否合法? [英] std::vector<std::wstring> Is moving/reallocating inner wstring.data() legal?

查看:93
本文介绍了std :: vector< std :: wstring>移动/重新分配内部wstring.data()是否合法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是节选:

 <代码> ...std :: vector< std :: wstring>vecWstr;vecWstr.emplace_back(L"1");wchar_t * data1 = vecWstr [0] .data();//<-此指针将来需要使用.vecWstr.emplace_back(L"2");wchar_t * data2 = vecWstr [0] .data();如果(data1!= data2)MessageBox(L错误,不相等.",L比较"); 

MessageBox 总是出现.
因此,在这里,我比较了 .emplace()之前和之后的两个 wstring 缓冲区.以我的理解,它们必须相等.

这里主要关心的是:为什么 vector 在一秒钟后移动/重新分配了第一内部元素 std :: wstring 元素?
在进行了怪异的程序行为后进行了调查之后,出现了这个问题.
如果我在第二个 .emplace()之前保存 vecWstr [0] .data()缓冲区指针,则缓冲区指针将变得过时并且程序的行为不当.
最大的问题是程序中有很多 std :: vector< std :: wstring> ,但它们似乎都按预期工作,到目前为止,只有一个如所示以上.
这一切都在 MSVS 16.1.5

问题是:
谁在这里? std :: vector 是否可以更改/移动其 std :: wstring 元素的内部缓冲区?

解决方案

在C ++ STL中,有一些称为指针无效的东西.这意味着,当您获得容器中某个元素的指针,然后又修改了该容器时,在修改之后,您的指针可能不再有效.

指针无效的规则由标准定义,在容器与容器之间,操作与操作之间有所不同.

在您的情况下,您有一个 std :: vector .如果您 emplace_back 并且向量需要更大的容量来添加元素,则对向量元素的引用/指针/迭代器将不再有效.在这种情况下,向量将在内存中分配另一个更大的空间,并将所有元素移到那里.

但是等等!

您是直接从字符串中获取 data()指针!为什么此指针也无效? wstring 不应该是仅包含指向某些堆缓冲区的指针的轻量级结构吗?

好吧,这是SSO(小字符串优化)的魔力.如果您的字符串足够小,则 wstring 只会将其缓冲区存储在数据结构本身中(而不是存储指向缓冲区的指针).在这种情况下,当您移动它时,指针当然会失效.

您的字符串非常小(1个宽字符),因此它满足SSO的条件.如果您使用更长的版本:

  std :: vector< std :: wstring>vecWstr;vecWstr.emplace_back(L"asdfghjkl");wchar_t * data1 = vecWstr [0] .data();//<-此指针将来需要使用.vecWstr.emplace_back(L"qwertyuiop");wchar_t * data2 = vecWstr [0] .data();如果(data1!= data2)MessageBox(0,L错误,不相等.",L比较",0);返回0; 

该消息框可能不会弹出.

但是,您无法控制运行时字符串的长度,并且您不知道编译器将如何实现SSO,所以请不要这样编码!

相反,您可以使用 reserve 方法(如songyuanyao所建议),或使用其他在添加元素时不会使指针无效的容器.请参阅 std :: list ... 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 always arises.
So, here i compare two wstring buffers before and after.emplace(). In my understanding they must be equal.

The main concern here is: Why vector moves/reallocates 1st innner std::wstring element after emplacing a second?
This question arose after investigation took place after weird program behavior.
If i save vecWstr[0].data() buffer pointer before second .emplace() then buffer pointer becomes obsolete and program behaves inappropriately.
The biggest issue is that there are a lot of std::vector<std::wstring>'s in a program, but all of them seem to work as expected, and only one so far like showed above.
This is all in MSVS 16.1.5

The question is:
Who is right here? Can std::vector change/move the inner buffer of its std::wstring elements or not?

解决方案

In C++ STL, there is something called pointer invalidation. This means, when you obtain a pointer of an element in a container, and later you modified the container, after the modification your pointer may be no longer valid.

The rule of pointer invalidation is defined by the standard and varies between containers to containers, operations to operations.

In your case, you have a std::vector. A reference/pointer/iterator to a vector element is no longer valid if you emplace_back and vector needs bigger capacity for the added element. In this case, the vector allocate another bigger space in memory and moves all its elements there.

But wait!

You are taking data() pointer directly from the string! Why is this pointer also invalidated? Shouldn't wstring be a light-weight struct that just contains pointer to some heap buffer?

Well, this is the magic of SSO (Small String Optimization). If your string is small enough, wstring just stores its buffer in the data structure itself (rather than storing a pointer to a buffer) . In this case, when you move it around, of course the pointer is invalidated.

Your string is pretty small (1 wide char), so it satisfies the condition of SSO. If you use longer ones:

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;

The messagebox would probably not pop.

However you don't have control over the length of the run-time string, and you don't know how your compiler would implement SSO, so don't code this way!

Instead, you can use the reserve method (as songyuanyao suggested), or use other containers that don't invalidate pointers when adding element. Please refer to std::list and std::deque. Read the sections about their pointer/reference/iterator invalidation.


这篇关于std :: vector&lt; std :: wstring&gt;移动/重新分配内部wstring.data()是否合法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆