在std :: move()之后使字符串变空的机制

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要清空,但我的自我定义不是?

Wyz*_*a-- 5

因为这就是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)

这会将内容传输到新字符串,并使旧字符串处于默认构造函数创建它的状态.这避免了分配另一个数组并将旧数组复制到其中的需要; 相反,现有的数组只是获得一个新的所有者.