一文搞定移动语义和完美转发
浅拷贝和深拷贝
简单的区分: 浅拷贝:按字节拷贝,如果是指针变量则直接对指针地址进行拷贝 深拷贝:对内容进行拷贝,如果有指针变量则另外申请地址拷贝所指内容
写c++类时,如果类中有指针成员的话,通常我们都会格外注意,要注意拷贝构造函数的编写,因为如果简单的浅拷贝可能导致free一块未定义区域,是非法的。
如下面的程序:
#include<iostream>
using namespace std;
class HasPtrMem
{
public:
HasPtrMem() :p(new int(0)) {
}
HasPtrMem(const HasPtrMem& h) :p(h.p) {
}
~HasPtrMem() {
delete p; }
int* p;
};
int main()
{
HasPtrMem a;
HasPtrMem b(a);
cout << *a.p << endl;
cout << *b.p << endl;
}
构造函数申请了一块堆空间,而析构函数对该空间进行释放。在拷贝构造函数中,执行了浅拷贝,这会引发一个非常严重的问题:a的p申请了一块区域,对b执行了拷贝构造,则b的p指向了同一块区域,因此当程序执行结束时,a和b将纷纷执行其析构函数,假设a先执行其析构函数,则a将所指的区域进行了释放,那么释放后b的指针所指向的区域就成为了非法内存区域,即成为了dangling pointer 悬挂指针,对悬挂指针执行free释放内存将导致严重错误。
因此,通常我们会采用深拷贝的方法解决该问题:
#include<iostream>
using namespace std;
class HasPtrMem
{
public:
int* p;
HasPtrMem() :p(new int(0)) {
}
HasPtrMem(const HasPtrMem& h) :p(new int(*h.p)){
} //将拷贝构造函数改成深拷贝,重新申请一块堆上的内存,然后用h.p进行初始化
~HasPtrMem() {
delete p; }
};
int main()
{
HasPtrMem a;
HasPtrMem b(a);
cout << *a.p << endl;
cout << *b.p << endl;
}
通过深拷贝,便可以解决浅拷贝的问题。使用深拷贝时,另外申请了一块区域并通过需要拷贝的对象的内容进行初始化,也就是其实指向的是不同的区域,只是内容相同。
看样子深拷贝完美解决了该问题,但是事实上确实这么完美吗?
移动语义
拷贝构造函数为指针成员进行内容拷贝的做法几乎是完美的,但是有些情况下该方法还是较为繁琐。下面的例子将说明:
#