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语义去提高性能。

京公网安备 11010502036488号