移动构造函数,便是在用原对象指针对新对象指针进行赋值后,将原对象成员指针置为空指针

C++11新特性学习笔记之移动构造函数
指针成员和浅拷贝
通常来讲,若是一个类中有指针成员,则要当心拷贝成员函数的编写,由于若是不注意,则会形成程序的内存泄漏。以下所示的例子。ios

#include <iostream>函数

class HasPtrMem{
public:
HasPtrMem() : m_data(new int(0)){}
~HasPtrMem(){
if (m_data != nullptr)
{
delete m_data;
m_data = nullptr;
}
}
int *m_data;
};性能

int main(){
HasPtrMem a;
HasPtrMem b(a);
std::cout << a.m_data << std::endl;//0
std::cout << b.m_data << std::endl;//0
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
咱们申明了一个带有指针成员的HasPtrMem类,在main函数中声明了对象a,再用对象a去初始化b,因为类中没有显示声明拷贝构造函数,则按照C++语法会调用编译器隐式生成的拷贝构造函数。这样就会出现如下一个问题:学习

a和b的m_data和都指向同一块堆内存,所以在main做用域结束后,a和b的析构函数依次被调用,好比b.m_data所指向的内存先被释放后,a.m_data就没法再对其进行操做,从而致使内存泄漏。.net

上述拷贝构造方式就是浅拷贝(shallow copy):只对数据成员进行简单的赋值。指针

深拷贝
深拷贝(deep copy):针对存在指针数据成员的状况下,从新分配内存空间,再也不是简单的指针赋值。以下所示。对象

#include <iostream>
class HasPtrMem{
public:
HasPtrMem() : m_data(new int(0)){}
HasPtrMem(HasPtrMem& h) : m_data(new int(*h.m_data)){} //拷贝构造函数,从堆中分配内存,用h.m_data初始化
~HasPtrMem(){
if (m_data != nullptr)
{
delete m_data;
m_data = nullptr;
}
}
int *m_data;
};blog

int main(){
HasPtrMem a;
HasPtrMem b(a);
std::cout << *a.m_data << std::endl;//0
std::cout << *b.m_data << std::endl;//0
} //正常析构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
运行结果:
0
0内存

上述结果就不会报错,新的拷贝构造函数从堆中分配心内存,将分配来的内存的指针交给m._data,经过这样的方法,就能避免产生悬挂指针(dangling pointer)。作用域

移动构造函数
若是指针所指向很是大的内存数据的话,则拷贝构造的代价就很是昂贵,会极大地影响性能。C++11提供一种简洁解决方法:移动构造函数,便是在用原对象指针对新对象指针进行赋值后,将原对象成员指针置为空指针,使得其没法指向内存数据,从而保证在析构的时候不会产生内存泄漏。这样既不用分配新内存,也不会产生内存泄漏,从而很好地解决了上述问题。以下所示。

#include <iostream>

class HasPtrMem{
public:
HasPtrMem() : m_data(new int(0)){
std::cout << "Construct: " << ++n_cstr << std::endl;
}
HasPtrMem(const HasPtrMem &h) : m_data(new int(*h.m_data)){
std::cout << "Copy construct: " << ++n_cptr << std::endl;
}
HasPtrMem(HasPtrMem&& h) :m_data(h.m_data){//移动构造函数
h.m_data = nullptr; //将临时值的指针成员置空
std::cout << "Move construct: " << ++n_mvtr << std::endl;
}
~HasPtrMem(){
if (m_data != nullptr)
{
delete m_data;
m_data = nullptr;
}
std::cout << "Destruct: " << ++n_dstr << std::endl;
}
int *m_data;
static int n_cstr;
static int n_dstr;
static int n_cptr;
static int n_mvtr;
};
int HasPtrMem::n_cstr = 0;
int HasPtrMem::n_cptr = 0;
int HasPtrMem::n_mvtr = 0;
int HasPtrMem::n_dstr = 0;

HasPtrMem GetTemp(){
HasPtrMem h;
std::cout << "Resource from " << __FUNCTION__ << ":" << std::hex << h.m_data << std::endl;
return h;
}

int main()
{
HasPtrMem a = GetTemp();
std::cout << "Resource from " << __FUNCTION__ << ":" << std::hex << a.m_data << std::endl;
system("pause");
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
运行结果:

Construct: 1Resource from GetTemp:000000E2E9B55D00Move construct: 1Destruct: 1Resource from main:000000E2E9B55D00Destruct: 2123456GetTemp()函数返回了h的一个临时变量时,调用了移动构造函数,其中的h和main中的a的指针成员值是相同的,说明了h.m _data和a.m _data都指向了相同的堆内存地址.所以其指针在GetTemp函数返回时免于被析构的“命运”,成为main中a的变量。

相关文章
相关标签/搜索