再最近的工程中,发现有滥用std::move的现象,现在对std::move在不同场景下的作用做一个简单分析与总结。
在c++编程中,为了提高程序运行的效率,可使用std::move来将一个左值转变成右值,或者说让被std::move包裹的变量具有可移动属性,能够将自身的持有的资源转移到另一个对象中。
那么何时该使用std::move呢,接下来我将通过一系列的试验程序来探索std::move在不同情况下的表现。
由基本类型构成的结构体
我们先定义一个由基本类型构成的结构体
struct TestStruct1{
int a;
char b;
};
然后测试TestStruct1在std::move中的具体表现:
TestStruct1 t1;
t1.a = 10;
t1.b = 'a';
printf("t1 value: a = %d b = %c t1 address:a = %0x b = %0x \n",
t1.a,t1.b,&t1.a,&t1.b);
TestStruct1 t2(std::move(t1));
printf("t1 value: a = %d b = %c t1 address:a = %0x b = %0x \n",
t1.a,t1.b,&t1.a,&t1.b);
printf("t2 value: a = %d b = %c t2 address:a = %0x b = %0x \n",
t2.a,t2.b,&t2.a,&t2.b);
获得输出:
t1 value: a = 10 b = a t1 address:a = 4feb39f8 b = 4feb39fc
t1 value: a = 10 b = a t1 address:a = 4feb39f8 b = 4feb39fc
t2 value: a = 10 b = a t2 address:a = 4feb3a00 b = 4feb3a04
可以看到对基本的类型构造时使用std::move,其效果跟普通的赋值没有任何区别。
拥有指针的结构体
接下来我们让结构体中包含基本类型的指针
struct TestStruct2{
int* a;
char* b;
};
我们对这个结构体使用std::move,并查看输出
TestStruct2 t1{new int[1],"Hello"};
*(t1.a) = 2;
printf("t1 value: a = %d b = %s t1 address:a = %0x b = %0x \n",
*(t1.a),t1.b,t1.a,t1.b);
TestStruct2 t2(std::move(t1));
printf("t1 value: a = %d b = %s t1 address:a = %0x b = %0x \n",
*(t1.a),t1.b,t1.a,t1.b);
printf("t2 value: a = %d b = %s t2 address:a = %0x b = %0x \n",
*(t2.a),t2.b,t2.a,t2.b);
t1 value: a = 2 b = Hello t1 address:a = 6c948e70 b = 6bed1b6e
t1 value: a = 2 b = Hello t1 address:a = 6c948e70 b = 6bed1b6e
t2 value: a = 2 b = Hello t2 address:a = 6c948e70 b = 6bed1b6e
可以看出使用std::move的效果跟普通的赋值效果没有任何区别;
对string使用std::move
当对string使用std::move来调用其移动构造函数时,根据字符串长度的不同,会右不同的输出结果:
字符串较短时
std::string t1 = "Hello";
printf("t1 value:%s t1 address %0x \n",t1.c_str(),t1.c_str());
std::string t2(t1);
printf("t2 value:%s t2 address %0x \n",t2.c_str(),t2.c_str());
std::string t3(std::move(t1));
printf("t1 value:%s t1 address %0x \n",t1.c_str(),t1.c_str());
printf("t3 value:%s t3 address %0x \n",t3.c_str(),t3.c_str());
t1 value:Hello t1 address ec190820
t2 value:Hello t2 address ec190840
t1 value: t1 address ec190820
t3 value:Hello t3 address ec190860
字符串较长时
t1 value:Helloooooooooooooooooooooo t1 address 1e070e70
t2 value:Helloooooooooooooooooooooo t2 address 1e0712b0
t1 value: t1 address 87ef6a90
t3 value:Helloooooooooooooooooooooo t3 address 1e070e70
可以看到当字符串较短时,t3中字符串的地址跟t1中不相同,而字符串较长时t3中字符串的地址跟t1中的相同,意味着当字符串较长是,通过string的移动构造函数会直接将t1中的字符串地址给t3。
总结
从上述试验中可以看出,std::move的作用是将一个左值变成一个右值,以供移动构造函数使用。
如果一个变量并没有定义移动构造函数,那么其将调用默认的移动构造函数,其功能与普通的构造函数相同(如果变量内部的成员变量也没定义移动构造函数的话);
因此std::move应该用在自定义了移动构造函数的那些变量中。