wal*_*ala 2 c++ move-semantics c++11
关于如何std::move()真正空洞的东西,我有些困惑.
我写了一些代码:
int main()
{
string str1("this is a string");
std::cout<<std::boolalpha<<str1.empty()<<std::endl;
string str2(std::move(str1));
cout<<"str1: "<<str1.empty()<<endl;
cout<<"str2: "<<str2.empty()<<endl;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
假
true //这意味着原始字符串被清空
假
为什么每次都会清空原始字符串?我已经阅读了一些关于移动语义的材料,包括它的原始提议(这一个),它说:
副本和移动之间的区别在于副本保持源不变.另一方面,移动使源处于针对每种类型定义不同的状态.源的状态可以不变,或者可以完全不同.唯一的要求是对象保持自我一致状态(所有内部不变量仍然完好无损).从客户端代码的角度来看,选择move而不是copy意味着您不关心源的状态会发生什么.
所以,根据这个词,上面str1的原始内容应该是某种未定义的.但为什么每次都这样move(),它就被清空了?(实际上我在两者上测试了这个行为std::string,std::vector但结果是一样的.)
要了解更多信息,我将定义自己的字符串类进行测试,如下所示:
class mstring
{
private:
char *arr;
unsigned size;
public:
mstring():arr(nullptr),size(0){}
mstring(char *init):size(50)
{
arr = new char[size]();
strncpy(arr,init,size);
while(arr[size-1] != '\0') //simply copy
{
char *tmp = arr;
arr = new char[size+=50]();
strncpy(arr,tmp,50);
delete tmp;
strncpy(arr-50,init+(size-50),50);
}
}
bool empty(){ return size==0;}
}
Run Code Online (Sandbox Code Playgroud)
做同样的事情:
int main()
{
mstring str("a new string");
std::cout<<std::boolalpha<<str.empty()<<std::endl;
mstring anotherStr(std::move(str));
std::cout<<"Original: "<<str.empty()<<std::endl;
std::cout<<"Another: "<<anotherStr.empty()<<std::endl;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
假
原文:flase //表示原始字符串仍然存在
另:错误
即使我添加这样的移动构造函数:
mstring(mstring&& rvalRef)
{
*this = rvalRef;
}
Run Code Online (Sandbox Code Playgroud)
结果仍然相同.我的问题是:为什么std::string要清空,但我的自我定义不是?
因为这就是std::string移动构造函数的实现方式.它取得旧字符串内容的所有权(即动态分配的char数组),而旧字符串则没有任何内容.
mstring另一方面,您的类实际上并不实现移动语义.它有一个移动构造函数,但它只是使用复制字符串operator=.更好的实施方式是:
mstring(mstring&& rvalRef): arr(rvalRef.arr), size(rvalRef.size)
{
rvalRef.arr = nullptr;
rvalRef.size = 0;
}
Run Code Online (Sandbox Code Playgroud)
这会将内容传输到新字符串,并使旧字符串处于默认构造函数创建它的状态.这避免了分配另一个数组并将旧数组复制到其中的需要; 相反,现有的数组只是获得一个新的所有者.