移动语义
移动语义是C++11引入的一个概念,本质很简单,就是利用右值引用,将资源直接“窃取到自身”,而不是进行深层拷贝。
概念可能有点抽象,我们来看个例子就清楚了。下面的代码中,MyString类中有四个构造函数。前两个都是普通构造函数,重点在后面两个:拷贝构造和移动构造。
class MyString
{
public:
MyString() = default;
MyString(const char* string)
{
printf("created!\n");
m_Size = strlen(string);
m_Data = new char[m_Size];
memcpy(m_Data, string, m_Size);
}
//拷贝构造
MyString(const MyString& other)
{
printf("copied!\n");
m_Size = other.m_Size;
m_Data = new char[m_Size];
memcpy(m_Data, other.m_Data, m_Size);
}
//移动构造
MyString(MyString&& other) noexcept
{
printf("moved!\n");
//仅仅是接管了other的数据,并不像拷贝构造那样还需要分配新的内存来进行深拷贝
m_Size = other.m_Size;
m_Data = other.m_Data;
other.m_Size = 0;
other.m_Data = nullptr;
}
~MyString()
{
printf("destroyed!\n");
delete m_Data;
}
private:
char* m_Data;
uint32_t m_Size;
};
int main(int argc, char* argv[])
{
std::vector<MyString> vec;
MyString my("xiaoxiaoniao");
vec.push_back(my);
//vec.push_back(std::move(my));
}
上面代码中,执行vec.push_back(my)时,会发生什么呢?
啥也不发生,仅仅是将my推入vec中?No,它会创建一个新的MyString对象,将新的对象推入vec中。问题是,会调用哪个构造函数呢?
会调用MyString的拷贝构造函数。而在拷贝构造里,使用new为m_Data重新开辟了堆空间!
但我们回过头来想想,我们仅仅是想把my中的数据放到vec中,为什么还要重新分配堆空间?直接将my中的数据接管过来不可以吗?
当然可以,这就是移动构造!直接接管my对象的数据,然后将my对象中的数据清空,就实现了移动构造。无需重新为MyString类中m_Data重新分配空间,提高了内存的管理效率!
通俗来说,移动构造就像是 没有中间商赚差价了。
当然,我们需要使用std::move函数告知编译器,变量可以当做右值处理,这样才会调用移动构造。
std::move本质其实就是static_cast<decltype(obj)&&>(obj),当然,真实实现要复杂一些。