我很惊讶s和s2内部指针指向"示例"不平等,有什么解释?
#include <string>
#include <cassert>
int main()
{
std::string s("sample");
std::string s2(std::move(s));
assert(
reinterpret_cast<int*>(const_cast<char*>(s.data())) ==
reinterpret_cast<int*>(const_cast<char*>(s2.data()))
); // assertion failure here
return 1;
}
答案 0 :(得分:3)
为什么你认为他们应该是一样的?您正在使用其移动构造函数从s2
构建s
。这会将数据所有权从s
转移到s2
,并使s
处于“空”状态。标准没有详细说明这是什么,但在此之后访问s
的数据(不先重新分配)是未定义的。
string
的简化(和不完整)版本可能如下所示:
class string {
char* buffer;
public:
string(char const* from)
: buffer(new char[std::strlen(from) + 1])
{
std::strcpy(buffer, from);
}
string(string&& other)
: buffer(other.buffer)
{
other.buffer = nullptr; // (*)
}
~string() {
delete[] buffer;
}
char const* data() const { return buffer; }
};
我希望这能说明data()
成员不相等的原因。如果我们省略了(*)
标记的行,我们会在main
结束时删除内部缓冲区两次:s
一次,s2
一次。重置buffer
指针可确保不会发生这种情况。
答案 1 :(得分:3)
每个std::string
都有自己指向的缓冲区。您从另一个移动的事实不会使它共享一个缓冲区。初始化s2
时,它将从s
接管缓冲区并成为该缓冲区的所有者。为了避免s
“拥有”相同的缓冲区,它只是将s
的缓冲区设置为一个新的空缓冲区(现在由s
负责)。
从技术上讲,还有一些优化,很可能没有为空字符串或非常小字符串显式分配的实际缓冲区,而是std::string
的实现将使用std::string
的一部分1}}记忆本身。这通常称为STL中的小字符串优化。
另请注意,s
已被移除,因此您的代码访问其数据是非法的,这意味着可以返回任何内容。
答案 2 :(得分:2)
在使用某个已知值替换其值之前,不应使用移动的string
:
库代码需要在参数中保留有效值,但除非类型或函数文档另有说明,否则对结果参数值没有其他约束。这意味着避免再次使用移动参数通常是最明智的。如果必须再次使用它,请确保在执行此操作之前使用已知值重新初始化它。
库可以将任何想要的东西粘贴到字符串中,但很可能你最终会得到一个空字符串。这就是运行example from cppreference产生的东西。但是,人们不应该期望在移动的对象中找到任何特别的东西。