所以这里有一个小型测试程序:
#include <string>
#include <iostream>
#include <memory>
#include <vector>
class Test
{
public:
Test(const std::vector<int>& a_, const std::string& b_)
: a(std::move(a_)),
b(std::move(b_)),
vBufAddr(reinterpret_cast<long long>(a.data())),
sBufAddr(reinterpret_cast<long long>(b.data()))
{}
Test(Test&& mv)
: a(std::move(mv.a)),
b(std::move(mv.b)),
vBufAddr(reinterpret_cast<long long>(a.data())),
sBufAddr(reinterpret_cast<long long>(b.data()))
{}
bool operator==(const Test& cmp)
{
if (vBufAddr != cmp.vBufAddr) {
std::cout << "Vector buffers differ: " << std::endl
<< "Ours: " << std::hex << vBufAddr << std::endl
<< "Theirs: " << cmp.vBufAddr << std::endl;
return false;
}
if (sBufAddr != cmp.sBufAddr) {
std::cout << "String buffers differ: " << std::endl
<< "Ours: " << std::hex << sBufAddr << std::endl
<< "Theirs: " << cmp.sBufAddr << std::endl;
return false;
}
}
private:
std::vector<int> a;
std::string b;
long long vBufAddr;
long long sBufAddr;
};
int main()
{
Test obj1 { {0x01, 0x02, 0x03, 0x04}, {0x01, 0x02, 0x03, 0x04}};
Test obj2(std::move(obj1));
obj1 == obj2;
return 0;
}
我用于测试的软件:
编译器:gcc 7.3.0
编译器标志:-std = c ++ 11
OS:Linux Mint 19(塔拉)和上游发行版Ubuntu 18.04 LTS(仿生)
我在这里看到的结果是,移动后,向量缓冲区仍然具有相同的地址,但字符串缓冲区没有。因此,在我看来,它分配了新的,而不只是交换缓冲区指针。是什么原因导致这种行为?
答案 0 :(得分:36)
您可能会看到small/short string optimization的影响。为了避免对每个微小的小字符串进行不必要的分配,许多std::string
的实现都包括一个固定大小的小型数组,以容纳小的字符串而无需使用new
(该数组通常用于其他一些不需要的成员)在没有使用动态分配的情况下是必需的,因此无论是小型string
还是大型std::move
,它消耗很少或没有额外的内存来提供它,并且这些字符串不能从g++
中受益(但是它们很小,所以很好)。较大的字符串将需要动态分配,并且将按您期望的那样传递指针。
仅用于演示,此代码位于void move_test(std::string&& s) {
std::string s2 = std::move(s);
std::cout << "; After move: " << std::hex << reinterpret_cast<uintptr_t>(s2.data()) << std::endl;
}
int main()
{
std::string sbase;
for (size_t len=0; len < 32; ++len) {
std::string s1 = sbase;
std::cout << "Length " << len << " - Before move: " << std::hex << reinterpret_cast<uintptr_t>(s1.data());
move_test(std::move(s1));
sbase += 'a';
}
}
:
NUL
产生高(堆栈)地址,该地址在长度为15或更短(随体系结构指针大小而变化)的移动构造上改变,但是一旦到达长度16或更高长度,切换到低(堆)地址,这些地址在移动构造后保持不变(由于C ++ 11及更高版本需要此开关,因此开关在16,而不是17,因为它是<html>
<head>
<meta charset="UTF-8" />
<style>
.img {
display: block;
width: 412px;
height: 183px;
}
</style>
</head>
<body style="margin: 0px">
<img class="img" alt="" src="https://dummyimage.com/412x183/2c38bd/000000">
<img class="img" alt="" src="https://dummyimage.com/412x183/2c38bd/000000">
</body>
</html>
终止字符串)。
要100%清除:这是一个实现细节。 C ++规范的任何部分都没有要求这种行为,因此,您完全不应该依赖它发生,并且当它出现时,对于特定的字符串长度,您也不应该依赖它发生。