C++11中引入的移动语义,这个move操作是否会改变对象的地址,测试程序如下:
1 #include <iostream> 2 #include <string> 3 #include <cstdio> 4 5 using namespace std; 6 7 string func(int n) 8 { 9 string s = ""; 10 for(int i = 0; i < n; i++) 11 s += ('A' + i); 12 cout << "in func , s : " << s << std::endl; 13 printf("in func , s arrd : %lu ", (long unsigned int)&s); 14 printf("in func , s data addr : %lu ", (long unsigned int)s.data()); 15 16 return std::move(s); 17 } 18 19 20 int main() 21 { 22 string ret = func(30); 23 24 std::cout << "ret : " << ret << std::endl; 25 printf("in main , ret addr : %lu ", (long unsigned int)&ret); 26 printf("in main , ret data addr : %lu ", (long unsigned int)ret.data()); 27 28 return 0; 29 }
运行结果:
可以看到,s的地址和ret的地址是不一样的,而它们底层引用的数据的地址是一样的。
也就是说元数据的地址会改变,但是底层的真正存储数据的地址不变,这也是move的作用,减少底层的数据的拷贝。
例子2:
1 #include <iostream> 2 #include <cstdio> 3 4 using namespace std; 5 6 class test_data 7 { 8 public: 9 test_data() 10 { 11 std::cout << "test_data()" << std::endl; 12 } 13 14 test_data(const test_data& d) 15 { 16 std::cout << "test_data(const test_data& d)" << std::endl; 17 } 18 19 ~test_data() 20 { 21 std::cout << "~test_data()" << std::endl; 22 } 23 }; 24 25 26 test_data func() 27 { 28 test_data t; 29 30 return t; 31 } 32 33 int main() 34 { 35 test_data ret = func(); 36 37 return 0; 38 }
结果1:
结果一中,编译时禁止rvo优化,可以看到经历了一次构造,和两次拷贝构造。
一次构造是28行的t构造,拷贝构造是func返回时,将结果拷贝构造给了临时对象,紧接着t析构,临时对象又拷贝构造给了ret,接着临时对象析构,最后ret析构。
不禁止rvo优化的话,结果如下:
下面添加move:
1 #include <iostream> 2 #include <cstdio> 3 4 using namespace std; 5 6 class test_data 7 { 8 public: 9 test_data() 10 { 11 std::cout << "test_data()" << std::endl; 12 } 13 14 test_data(const test_data& d) 15 { 16 std::cout << "test_data(const test_data& d)" << std::endl; 17 } 18 19 ~test_data() 20 { 21 std::cout << "~test_data()" << std::endl; 22 } 23 }; 24 25 26 test_data func() 27 { 28 test_data t; 29 30 return std::move(t); 31 } 32 33 int main() 34 { 35 test_data ret = func(); 36 37 return 0; 38 }
第30行添加了move。注意,没有实现移动构造。
结果1:
禁止rvo优化时加不加move都一样。
结果2:
开了rvo优化,只是少了临时对象的构造。
现在看来,开了rvo优化不用move的时候,优化力度更强一些,那个时候只有一次构造和一次析构。
使用move时,类应该有对应的移动构造函数才可以,新代码:
1 #include <iostream> 2 #include <cstdio> 3 4 using namespace std; 5 6 class test_data 7 { 8 public: 9 test_data() 10 { 11 p = new char[1024]; 12 std::cout << "test_data()" << std::endl; 13 } 14 15 test_data(const test_data& d) 16 { 17 p = new char[1024]; 18 std::cout << "test_data(const test_data& d)" << std::endl; 19 } 20 21 test_data(const test_data&& d) 22 { 23 p = d.p; 24 const_cast<test_data&>(d).p = nullptr; 25 std::cout << "test_data(const test_data&& d)" << std::endl; 26 } 27 28 ~test_data() 29 { 30 delete p; 31 std::cout << "~test_data()" << std::endl; 32 } 33 34 public: 35 char *p; 36 }; 37 38 39 test_data func() 40 { 41 test_data t; 42 43 std::cout << "in func , t addr : " << (long unsigned int)&t << std::endl;; 44 std::cout << "in func , t data addr : " << (long unsigned int)t.p << std::endl;; 45 46 return std::move(t); 47 } 48 49 int main() 50 { 51 test_data ret = func(); 52 53 std::cout << "in main , ret addr : " << (long unsigned int)&ret << std::endl; 54 std::cout << "in main , ret data addr : " << (long unsigned int)ret.p << std::endl; 55 56 return 0; 57 }
结果:
如果只定义了移动构造而没有使用move,例如第46行直接是return t,这种情况下,编译器也会进行优化,还是会调用到移动构造。代码就改46这一行,运行结果:
依然走到了移动构造。
加上move只是让语义更加明确。