使字符串在std::move()之后为空的机制
mechanism that make a string empty after std::move() it
我对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//这意味着原始字符串被清空
错误
为什么每次都清空原始字符串?我读过一些关于移动语义的材料,包括它的原始提议(这一个),它说:
复制和移动的区别在于,复制会使源保持不变。另一方面,移动会使源处于为每种类型定义不同的状态。源的状态可能没有变化,或者它可能完全不同。唯一的要求是对象保持自一致状态(所有内部不变量仍然完好无损)。从客户端代码的角度来看,选择移动而不是复制意味着您不关心源代码的状态会发生什么。
因此,根据这个词,上面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] != ' ') //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//表示原始字符串仍在中
另一个:false
甚至我添加了一个类似于这样的移动构造函数:
mstring(mstring&& rvalRef)
{
*this = rvalRef;
}
结果仍然是一样的。我的问题是:为什么std::string
被清空了,而我自己定义的却没有?
因为std::string
移动构造函数就是这样实现的。它拥有旧字符串的内容(即动态分配的char
数组)的所有权,使旧字符串一无所有。
另一方面,mstring
类实际上并没有实现移动语义。它有一个move构造函数,但它所做的只是使用operator=
复制字符串。更好的实施方式是:
mstring(mstring&& rvalRef): arr(rvalRef.arr), size(rvalRef.size)
{
rvalRef.arr = nullptr;
rvalRef.size = 0;
}
这将内容传输到新字符串,并使旧字符串处于默认构造函数创建它的相同状态。这避免了分配另一个数组并将旧数组复制到其中的需要;相反,现有数组只得到一个新的所有者。
因此,根据这个词,上面
str1
的原始内容应该是某种未定义的。
关于状态应该是什么,绝对没有任何东西。规范说明了状态可以是什么:任何都定义得足够好,可以销毁或分配新值。
Empty符合任何条件,因此状态可以为空。
在这种情况下,这也是最有意义的。字符串本质上类似于
class string {
char *_M_data;
size_t _M_size;
size_t _M_alloc;
public:
...
}
其中_M_data是用new
分配的,并且必须用delete
删除(这可以用分配器参数自定义,但默认分配器只是这样做)。
现在,如果您不关心源的状态,那么最快的方法就是将缓冲区分配给目标,并用源中的nullptr
替换缓冲区(这样它就不会被删除两次)。没有缓冲区的字符串为空。
- 为什么在popback()操作之后,它仍然打印完整的矢量
- 在类定义之后定义一个私有方法
- 在循环C++中指定字符串之后,不会打印该字符串
- C++宏忽略之后的内容
- 要与"if constexpr"一起使用的编译时消息(在预处理器之后)
- strncpy之后的char数组的错误行为
- 计算十进制 c++ 之后的数字
- "x += x--"之后的 x 是什么?
- 类的前向声明之后的类成员函数定义,在类声明之前
- 为什么将双精度转换为 int 似乎在第 16 位数字之后将其四舍五入?
- execlp() 在 fork() 之后无法正常工作
- 我认为我的代码很好,但它在 cin a 之后停止并且没有进一步?
- 使用共享指针时,从共享指针本身释放内存的机制是什么
- 如何在MISRA C++之后实施CRTP
- 在 OpenCV 的 namedWindow 之前或之后初始化 Tesseract
- 检测到堆损坏:在正常块 c++ 动态 2D 数组之后
- C++ 如果在 if 为 true 之后运行,为什么还会这样做
- 在 fork() 之后,我在我的程序中不断得到相同的 pid
- 为什么 boost::comb 对结构化绑定的支持缺少结构化绑定机制对 boost::tuples::cons 的适应?
- 使字符串在std::move()之后为空的机制