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:
...
}