[英]I can't underestand how move semantics work to std::string
explicit Foo(std::string p_str) : m_str(std::move(p_str)) { ... }
I've made a constructor takes argument with move semantics. 我已经让构造函数接受了带有移动语义的参数。 (
p_str
-> m_str
) For a closer look, I opened the library header file basic_string.h
but there is one thing that I can't understand. (
p_str
> m_str
)为了更仔细地看,我打开了库头文件basic_string.h
但是有一点我不明白。
basic_string(basic_string&& __str) noexcept :
_M_dataplus(_M_local_data(), std::move(__str._M_get_allocator())) {
if (__str._M_is_local()) {
traits_type::copy(_M_local_buf, __str._M_local_buf, _S_local_capacity + 1);
} else {
_M_data(__str._M_data());
_M_capacity(__str._M_allocated_capacity);
}
_M_length(__str.length());
__str._M_data(__str._M_local_data()); // (#)
__str._M_set_length(0);
}
This is move constructor of basic_string
class. 这是
basic_string
类的move构造函数。 After (#)
is executed, p_str
turns to ".\\000\\000\\000\\000\\000\\000\\000..."
. 执行
(#)
后, p_str
变为".\\000\\000\\000\\000\\000\\000\\000..."
。 As I know, basic_string
stores strings in the array _M_local_buf
and method _M_local_data()
returns its address. 据我所知,
basic_string
将字符串存储在数组_M_local_buf
,方法_M_local_data()
返回其地址。
Then, why __str._M_data(__str._M_local_data());
然后,为什么要使用
__str._M_data(__str._M_local_data());
changes p_str
to zero-filled string? 将
p_str
更改为零填充字符串? Isn't p_str
( __str
) still having the original strings? 是不是
p_str
( __str
)仍具有原始字符串?
What you're seeing is short string optimization. 您所看到的是短字符串优化。
libstdc++'s std::string
implementation has two places it can store string data. libstdc ++的
std::string
实现有两个位置可以存储字符串数据。 It stores long strings in a dynamically allocated array of char
s. 它将长字符串存储在动态分配的
char
数组中。 To avoid the overhead of allocating that array, it can also store short strings in the space it usually uses to track the size of that dynamically allocated array. 为了避免分配该数组的开销,它还可以在通常用来跟踪该动态分配的数组的大小的空间中存储短字符串。 That's
_M_local_data
. 那是
_M_local_data
。
_M_dataplus._M_p
holds a pointer to the first element of whicherver buffer is currently in use. _M_dataplus._M_p
持有一个指向当前正在使用的哪个缓冲区的第一个元素的指针。 That's the pointer that _M_data()
returns, and the pointer that _M_data(pointer)
sets. 那是
_M_data()
返回的指针, _M_data(pointer)
设置的_M_data(pointer)
。
So putting that together, doing 所以放在一起,做
__str._M_data(__str._M_local_data());
__str._M_set_length(0);
is telling the moved-from string "Forget about any buffer you were previously managing, start using your local buffer for storage, and assume you hold no data". 告诉移出的字符串“忘记以前管理的任何缓冲区,开始使用本地缓冲区进行存储,并假设您不保存任何数据”。
Then, why
__str._M_data(__str._M_local_data());
然后,为什么要使用
__str._M_data(__str._M_local_data());
changesp_str
to zero-filled string?将
p_str
更改为零填充字符串?
It doesn't. 没有。 It changes
p_str
's data pointer to point to the memory that previously held the size of its externally-allocated array. 它将
p_str
的数据指针更改为指向以前保持其外部分配数组大小的内存。 What you're seeing are the leftovers of that capacity (Did p_str
have a capacity of 46
by chance? That's the ASCII code point for '.'
). 您看到的是该容量的剩余容量(
p_str
是否有46
的偶然容量?这是'.'
的ASCII代码点)。 For the moment between those two lines, p_str
is in an inconsistent state. 在这两行之间的时刻,
p_str
处于不一致状态。 Its size is still set to its previous size, but the buffer it now points to doesn't hold that number of characters. 它的大小仍然设置为以前的大小,但是它现在指向的缓冲区不包含该数量的字符。 That gets rectified on the next line when the move constructor sets its length to
0
. 当移动构造函数将其长度设置为
0
时,可以在下一行更正此错误。
Your std::string
stores its contents in one of two ways: 您的
std::string
通过以下两种方式之一存储其内容:
_M_local_buf
, which is presumably an array member of std::string
(and also accessible via _M_local_data()
). _M_local_buf
,该std::string
可能是std::string
的数组成员(也可以通过_M_local_data()
访问)。 char
array is allocated separately and a pointer to it is stored in _M_p
(which is also accessible via _M_data()
). char
数组,并将其指针存储在_M_p
(也可以通过_M_data()
访问)。 The if
statement deals with the two possible representations: if
语句处理两种可能的表示形式:
std::string
object ( __str._M_is_local()
), we copy the data into our own _M_local_buf
array. std::string
对象( __str._M_is_local()
)中,则将数据复制到我们自己的_M_local_buf
数组中。 Otherwise we simply copy the pointer (as returned by _M_data()
) and capacity. 否则,我们只复制指针(由
_M_data()
返回)和容量。
Right now our objects are in an inconsistent state: Both this->_M_data()
and __str._M_data()
point to the same allocated storage; 现在,我们的对象处于不一致状态:
this->_M_data()
和__str._M_data()
指向同一分配的存储; they both "own" it. 他们都“拥有”它。 To resolve this situation, we need to reset
__str
so it won't try to free our string data out from under us when it is destroyed. 要解决这种情况,我们需要重置
__str
以便在销毁字符串数据时不会尝试将其从我们下面释放出来。 We'll do that in a second. 我们将在一秒钟内完成。
_M_length(__str.length());
copies over the length value from __str
. 从
__str
复制长度值。
__str._M_data(__str._M_local_data());
makes _M_p
point to the internal array that is part of the __str
object. 使
_M_p
指向__str
对象的内部数组。 If __str
wasn't a short string to begin with, this array contains garbage. 如果
__str
开头不是短字符串,则此数组包含垃圾。 But that doesn't matter, because: 但这没关系,因为:
__str._M_set_length(0)
resets the length of __str
to 0. The contents of __str._M_local_buf
don't matter because 0 of them are "valid", ie part of the string. __str._M_set_length(0)
将__str
的长度重置为0 __str._M_local_buf
的内容无关紧要,因为其中0个为“有效”,即字符串的一部分。
In short: This move constructor copies the member variables of __str
into our new object, then makes __str
an empty string. 简而言之:此move构造函数将
__str
的成员变量复制到我们的新对象中,然后使__str
为空字符串。 (The string contents have been "moved" from __str
into *this
, so now __str
is empty.) (字符串内容已从
__str
“移动”到*this
,所以现在__str
为空。)
This is a general principle: Moving from a variable destroys its contents. 这是一个基本原则:从变量移出会破坏其内容。 It will be a valid object (a valid
std::string
in this case), but you cannot rely on any data still being there. 这将是一个有效的对象(在这种情况下为有效的
std::string
),但是您不能依赖仍然存在的任何数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.