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

C++ 使字符串在std::move()之后为空的机制,c++,c++11,move-semantics,C++,C++11,Move Semantics,我对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()<<end

我对
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;
}
做同样的事情:

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;
}
结果仍然是一样的。
我的问题是:
std::string
为什么被清空,而我的自定义却没有被清空?

因为这就是
std::string
move构造函数的实现方式。它拥有旧字符串内容的所有权(即动态分配的
char
数组),使旧字符串一无所获

另一方面,您的
mstring
类实际上并没有实现移动语义。它有一个移动构造函数,但它所做的只是使用
操作符=
复制字符串。更好的实施方式是:

mstring(mstring&& rvalRef): arr(rvalRef.arr), size(rvalRef.size)
{
  rvalRef.arr = nullptr;
  rvalRef.size = 0;
}
这会将内容传输到新字符串,并使旧字符串保持默认构造函数创建它时的状态。这避免了需要分配另一个数组并将旧数组复制到其中;相反,现有数组只会获得一个新的所有者

因此,根据这句话,上面的
str1
的原始内容应该是某种未定义的内容

国家应该是什么样子,这是完全不可能的。该规范说明了状态可以是什么:任何定义足够使其可以被销毁或分配新值的东西

Empty限定为any,因此状态可以为空

在这种情况下,这也是最有意义的。字符串本质上类似于

class string {
    char *_M_data;
    size_t _M_size;
    size_t _M_alloc;
public:
    ...
}
其中_M_数据是用
new
分配的,并且必须用
delete
删除(这可以通过分配器参数自定义,但默认分配器就是这样做的)


现在,如果您不关心源的状态,那么可以做的最快的事情就是将缓冲区分配给目标,并在源中用
nullptr
替换缓冲区(这样它就不会被删除两次)。没有缓冲区的字符串是空的。

您应该强调,这正是1)move构造函数的作用,2)std::basic_string(和其他标准库容器)的作用。这忽略了常见实现使用的小字符串优化的事实“另一方面,移动会使源处于为每种类型定义的不同状态。“-事实并非如此。对象处于未指定状态。
mstring(mstring&& rvalRef): arr(rvalRef.arr), size(rvalRef.size)
{
  rvalRef.arr = nullptr;
  rvalRef.size = 0;
}
class string {
    char *_M_data;
    size_t _M_size;
    size_t _M_alloc;
public:
    ...
}