1.临时对象作为右值引用减少拷贝构造
前面一些文章https://blog.nowcoder.net/detail/91bd120053d749448e8fabfd6b6db645 可能会疑惑,这个右值我不用也可以啊,为什么偏偏要发明这个东西,如果以下代码:
#include <iostream> #include <memory> #include <string> using namespace std; int g_constructNum = 0; int g_copyConstructNum = 0; int g_moveConstructNum = 0; int g_deleteNum = 0; class MyTestString { private: std::string m_data; int m_value; void test() {} public: MyTestString(std::string data,int value) :m_data(data),m_value(value) { std::cout << "construct: " << ++g_constructNum << std::endl; } MyTestString(const MyTestString& myTestString) { m_data = myTestString.m_data; m_value = myTestString.m_value; std::cout << "copy construct: " << ++g_copyConstructNum << std::endl; } ~MyTestString() { std::cout << "destruct: " << ++g_deleteNum << std::endl; } }; MyTestString getTempString() { MyTestString temp("tangmiao", 1); return temp; } int main() { MyTestString temp(getTempString()); return 0; }
如果编译器没有优化的话,那么结果应该是这个样子:
函数返回做一次拷贝构造,主函数里做一次拷贝构造。如果编译器做返回值优化,那么可能没有copy construct或者少。一些复杂的情况并也不是编译器能够优化的。这么多次拷贝构造又要***,又要删除临时对象,很浪费时间和空间。使用右值引用的话,就会延长临时对象的生命周期,并且采用移动构造函数,利用std::swap()来交换内存空间,而不是删除和新增,会减少很多开销。代码如下:
#include <iostream> #include <memory> #include <string> using namespace std; int g_constructNum = 0; int g_copyConstructNum = 0; int g_moveConstructNum = 0; int g_deleteNum = 0; class MyTestString { private: std::string m_data; int m_value; void test() {} public: MyTestString(std::string data,int value) :m_data(data),m_value(value) { std::cout << "construct: " << ++g_constructNum << std::endl; } MyTestString(const MyTestString& myTestString) { m_data = myTestString.m_data; m_value = myTestString.m_value; std::cout << "copy construct: " << ++g_copyConstructNum << std::endl; } MyTestString(MyTestString &&myTestString) { std::swap(m_data, myTestString.m_data); std::swap(m_value, myTestString.m_value); std::cout << "move copy construct: " << ++g_moveConstructNum << std::endl; } ~MyTestString() { std::cout << "destruct: " << ++g_deleteNum << std::endl; } }; MyTestString getTempString() { MyTestString temp("tangmiao", 1); return temp; } int main() { MyTestString temp(getTempString()); return 0; }
输出应为:
类似的std::vector库的右值引用用处:(来自如何评价 C++11 的右值引用(Rvalue reference)特性? - Tinro的回答 - 知乎
https://www.zhihu.com/question/22111546/answer/30801982 )
2.使用std::move直接将左值转化为右值
move是将对象的状态或者所有权直接从一个对象转移到另外一个对象,只是转移,没有内存拷贝,不会消耗太多资源。如下:
std::list<std::string> test; ... std::list<std::string> t=test;
将test东西完全拷贝到t里,性能较低。如下性能会提高很多:
std::list<std::string> test; ... std::list<std::string> t=std::move(test);
使用move几乎没有任何代价,只是转换了资源的所有权,实际上是将左值变成了右值引用,然后应用move语义调用构造函数,避免了拷贝,提高了程序的性能。当一个对象内部有较大的堆内存或者动态数组时,很有必要写move语义的拷贝构造函数和赋值函数(移动构造函数和移动赋值函数),避免无意义的深拷贝,提高性能。在C++STL库里很多地方都使用了move语义去提高性能。