C++--移动构造函数/移动赋值运算符
什么是移动语义?
在C++11中,移动语义是一个重要的新特性,它可以使程序在内存管理方面更加高效,同时也提高了程序的性能
它允许将一个对象的所有权从一个对象转移到另一个对象,而不需要进行数据的拷贝。
通俗理解
我有一份材料,A同学找我借,那我把材料复印一份,把复印件给他,这叫做数据拷贝;而我如果把材料的所有权转让给他,那么他现在直接拥有原始的那份材料,这叫做移动语义。
为什么需要移动语义?
拷贝
要理解为什么我们需要移动语义,那我们就需要理解拷贝的操作
C++中有拷贝构造函数和拷贝复制运算符。拷贝,顾名思义就是重新申请一块新的内存空间,然后将需要的数据复制一份放到里面。
如果要复制的对象中涉及到了其他对象或者是指针数据的话,那么拷贝操作就是一项耗时的过程。
我们通过一个例子来演示一下是如果进行拷贝的:
定义一个简单的类:
class Myclass
{
public:
Myclass(const std::string& s):str(s){};
private:
std::string str;
};
当新建一个对象A时,传递一个参数”hello,world”,A中的成员变量会存储该字符串,也就是会申请一个新的内存空间去存储该字符串:
Myclass A("hello,world");
当我们定义一个新的对象B,并将A赋值给B时:
Myclass B=A;
同样,B也会申请一段空间,并将A中存储的字符串拷贝过来
需要移动语义的情况
从拷贝的操作不难看出,这样的操作是耗时的,那在什么情况下,拷贝操作不是必要的呢?
同样还是延续上面的例子,这里我们定义一个容器以及一个对象tmp,然后将其装入到容器中:
std::vector<Myclass> myClasses;
Myclass tmp("hello");
myClasses.push_back(tmp);
myClasses.push_back(tmp);
每次将对象添加到容器中都会发生一次拷贝操作。
我们现在假设tmp对象在被添加到容器中两次之后就不再需要了,那第二次添加的时候是不是可以让容器直接取tmp对象的数据呢?这就是移动语义的意义,减少不必要的数据拷贝,提高程序的性能。
假设Myclass类已经实现了移动语义,我们就可以使用std::move
让myClasses容器直接转移tmp对象的数据为己用。
myClasses.push_back(tmp);
myClasses.push_back(std::move(tmp));
移动语义的实现
我们需要先了解右值引用
右值引用
我们都知道C++有一个操作叫引用,实际上默认指的是左值引用,也就是对一个左值进行引用。那右值引用就是对右值的引用
通过&&
声明,同时:
- 右值引用只能绑定到一个右值,不能绑定到左值;
- 右值引用可以通过
std::move()
将一个左值转换成右值引用
int a=0;
int&& tmp=a; //error,不能引用左值
int&& tmp2=0; //correct
移动构造函数
还是接着上面的例子,当向容器添加一个新元素时,如果是通过拷贝的方法,那么对应执行的就是拷贝构造函数,而如果是通过移动的方式,那对应的就是移动构造函数。
下面我们就为Myclass定义移动构造函数,其中string类型本身就实现了移动构造函数,所以可以直接使用std::move
:
Myclass(Myclass&& rValue) noexcept
:str(std::move(rValue.str))
{}
在移动构造函数中,我们要做的就是转移成员数据str。
我们就可以使用移动构造函数去创建新的对象而无需拷贝复制了:
Myclass tmp("hello");
Myclass A(std::move(tmp));
如果我们的类成员数据需要我们自己实现数据转移的话,也很简单,就是把数据拿过来,并将原先对象的数据清楚:
假设这个类有两个成员变量int 和 char*类型:
class Myclass2
{
private:
int data;
char* str;
public:
Myclass2()
:data(30)
{
str="mrdc";
}
;
Myclass2(Myclass2&& rValue) noexcept
:data(std::move(rValue.data))
{
rValue.data=0; //delete data
str=rValue.str; //transfer
rValue.str=nullptr; //delete str
}
~Myclass2()
{
if(str!=nullptr)
{
delete str;
str=nullptr;
}
};
};
通过移动构造函数创建对象B:
MyClass A{};
MyClass B{ std::move(A) }
内存中的布局:
移动赋值运算符
和移动构造函数的实现类似:
// 移动赋值运算符
MyClass& operator=(MyClass&& myClass) noexcept
{
val = myClass.val;
myClass.val = 0;
name = myClass.name;
myClass.name = nullptr;
return *this;
}
参考
https://bbs.huaweicloud.com/blogs/375866
标签:tmp,运算符,str,Myclass,拷贝,移动,构造函数 From: https://www.cnblogs.com/Mr-DC/p/18238771