当向量增长时,使用 std::move 而不是复制重新分配内存的好处?
Benefits of reallocating memory with std::move instead of copy when vector grows?
我正在阅读 Lippman 的 C++ Primer 第 5 版中的 StrVec
(字符串向量)示例,在讨论重新分配的部分中,他们提到 [=15 会更有效=] 所有向量对象 (strings
) 而不是复制。他写 "Our StrVec
's performance will be much better"。我理解这个推理,但我很好奇的是这实际上是如何影响内存中的对象的。
这个新分配的连续区域可能(保证?)内存中的其他地方,所以向量对象所在的旧区域和新区域是分离的,不是吗?
所以我们还需要将对象移动到内存中的这个新区域,这不构成内存中的副本吗?我也明白它让指向这个旧区域的指针有效,但其中的值是垃圾。我猜这可能是有益的?如果是这样,如何?是因为以后的内存分配可以使用那个区域吗?但这将如何使矢量实现更快?
我将尝试向
说明我的问题
如果向量在内存中看起来像这样:
A:[|strings|]
我们为它分配新的space
B:[----------------]
我们不需要复制字符串以便:
A:[xxxxxxxxx]
B:[|strings|-------]
我在这里错过了什么?
我承认移动语义对我来说并不明显,所以我想我已经错过了其中的明显好处。
更新
一定是累了,漏了同节的这句话:
For string
, we can imagine that each string
has a pointer to an
array of char
. Presumably the string
move constructor copies the
pointer rather than allocating space for and copying the characters
themselves.
实际的字符数据不在 std::vector
本身,而是在堆的其他地方。 std::string
只包含一些内务变量和指向字符数据的指针。因此,通过移动这些变量,重新分配的 std::vector
中的新 std::string
实例可以从原始 std::string
实例中窃取数据指针,而无需制作字符数据的新副本。
因为字符串本身包含指向更多缓冲区中的数据的指针!
A:[|||\]
||\ [String3]
|\ [String2]
| [String1]
[String0]
B:[....] //uninitialized
如果您复制 A 中的所有字符串,它们将依次复制其他四个缓冲区。但是,如果您 将字符串 移动到 B
,那么它们只会转移现有缓冲区的所有权。
字符数据并不存储在实际的 std::string
对象本身中,而是存储在 std::string
指向的内存中的其他地方。所以你的 std::vector
看起来更像这样:
[data1a] [data2a] [data3a]
^ ^ ^
| | |
VecA:[|string1a|string2a|string3a|...]
使用复制语义时,字符数据也必须复制到新内存中,例如:
[xxxxxx] [xxxxxx] [xxxxxx]
x x x
x x x
VecB:[|string1b|string2b|string3b|string4b|...]
| | | |
\/ \/ \/ \/
[data1b] [data2b] [data3b] [data4b]
改用移动语义时,新的 std::string
对象可以重新使用原始指针,而不必复制现有数据:
[data1a] [data2a] [data3a]
^ ^ ^
| | |
VecB:[|string1b|string2b|string3b|string4b|...]
|
\/
[data4b]
我正在阅读 Lippman 的 C++ Primer 第 5 版中的 StrVec
(字符串向量)示例,在讨论重新分配的部分中,他们提到 [=15 会更有效=] 所有向量对象 (strings
) 而不是复制。他写 "Our StrVec
's performance will be much better"。我理解这个推理,但我很好奇的是这实际上是如何影响内存中的对象的。
这个新分配的连续区域可能(保证?)内存中的其他地方,所以向量对象所在的旧区域和新区域是分离的,不是吗?
所以我们还需要将对象移动到内存中的这个新区域,这不构成内存中的副本吗?我也明白它让指向这个旧区域的指针有效,但其中的值是垃圾。我猜这可能是有益的?如果是这样,如何?是因为以后的内存分配可以使用那个区域吗?但这将如何使矢量实现更快?
我将尝试向
说明我的问题如果向量在内存中看起来像这样:
A:[|strings|]
我们为它分配新的space
B:[----------------]
我们不需要复制字符串以便:
A:[xxxxxxxxx]
B:[|strings|-------]
我在这里错过了什么? 我承认移动语义对我来说并不明显,所以我想我已经错过了其中的明显好处。
更新
一定是累了,漏了同节的这句话:
For
string
, we can imagine that eachstring
has a pointer to an array ofchar
. Presumably thestring
move constructor copies the pointer rather than allocating space for and copying the characters themselves.
实际的字符数据不在 std::vector
本身,而是在堆的其他地方。 std::string
只包含一些内务变量和指向字符数据的指针。因此,通过移动这些变量,重新分配的 std::vector
中的新 std::string
实例可以从原始 std::string
实例中窃取数据指针,而无需制作字符数据的新副本。
因为字符串本身包含指向更多缓冲区中的数据的指针!
A:[|||\]
||\ [String3]
|\ [String2]
| [String1]
[String0]
B:[....] //uninitialized
如果您复制 A 中的所有字符串,它们将依次复制其他四个缓冲区。但是,如果您 将字符串 移动到 B
,那么它们只会转移现有缓冲区的所有权。
字符数据并不存储在实际的 std::string
对象本身中,而是存储在 std::string
指向的内存中的其他地方。所以你的 std::vector
看起来更像这样:
[data1a] [data2a] [data3a]
^ ^ ^
| | |
VecA:[|string1a|string2a|string3a|...]
使用复制语义时,字符数据也必须复制到新内存中,例如:
[xxxxxx] [xxxxxx] [xxxxxx]
x x x
x x x
VecB:[|string1b|string2b|string3b|string4b|...]
| | | |
\/ \/ \/ \/
[data1b] [data2b] [data3b] [data4b]
改用移动语义时,新的 std::string
对象可以重新使用原始指针,而不必复制现有数据:
[data1a] [data2a] [data3a]
^ ^ ^
| | |
VecB:[|string1b|string2b|string3b|string4b|...]
|
\/
[data4b]