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

标签 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()<<endl;
    cout<<"str2: "<<str2.empty()<<endl;
}

输出为:

true//这意味着原始字符串被清空

为什么每次都是原来的字符串被清空? 我读过一些关于 move 语义的资料,包括它的原始提案(this one),其中说:

The difference between a copy and a move is that a copy leaves the source unchanged. A move on the other hand leaves the source in a state defined differently for each type. The state of the source may be unchanged, or it may be radically different. The only requirement is that the object remain in a self consistent state (all internal invariants are still intact). From a client code point of view, choosing move instead of copy means that you don't care what happens to the state of the source.

所以,根据这句话,上面的str1的原始内容应该是某种未定义。但为什么每次move()都会被清空呢? (实际上我已经在 std::stringstd::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;}

}

做同样的事情:

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;
}

输出为:

Original: flase//表示原来的字符串还在

另一个:错误

即使我添加了这样的 move 构造函数:

    mstring(mstring&& rvalRef)
    {
        *this = rvalRef;
    }

结果还是一样。 我的问题是:为什么 std::string 被清空,但我的自定义却没有?

最佳答案

因为这就是 std::string move 构造函数的实现方式。它获取旧字符串内容的所有权(即动态分配的 char 数组),而旧字符串什么也没有留下。

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

mstring(mstring&& rvalRef): arr(rvalRef.arr), size(rvalRef.size)
{
  rvalRef.arr = nullptr;
  rvalRef.size = 0;
}

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

关于c++ - 在 std::move() 之后使字符串为空的机制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34782270/

相关文章:

c++ - Brace-init-list 和赋值

c++ - 使用 ATOMIC_FLAG_INIT 和 std::atomic_flag::clear 有什么区别

c++11 - 如何实现作用域 guard 在作用域退出时恢复值(value)?

c++ - 隔离字符中的 1 字符串

c++ - 开始在 Visual Studio 2013 中使用 clang 3.6.0;如何解决我的 "unknown argument: -ftemplate-depth"编译器错误?

c++ - 什么是 std::promise?

C++11:未触发 move 构造函数

c++ type trait to say "trivially movable"- 例子

c++ - 无法编译简单的 C++ 程序,需要说明

c++ - 如何将 ANSI 字符 (char) 转换为 Unicode 字符 (wchar_t),反之亦然?