虚妄

C++11 智能指针 – Part 6 unique_ptr

    笔记     CPP·翻译

  1. 1. 什么是std::unique_ptr?
  2. 2. unique指针的独享所有权
  3. 3. 创建一个空的std::unique_ptr对象
  4. 4. 检查std::unique_ptr对象是否为空
  5. 5. 使用裸指针创建std::unique_ptr对象
  6. 6. 重置std::unique_ptr对象
  7. 7. std::unique_ptr对象无法被拷贝
  8. 8. 转移std::unique_ptr对象所有权
  9. 9. 释放裸指针

原文地址

译注: 本翻译只遵循文章要表达的意图,而不会逐句翻译。

这个是一系列关于智能指针的文章,谈及的东西都是比较入门的介绍。

什么是std::unique_ptr

智能指针出了std::shared_ptr,还包括另外一种std::unique_ptrstd::unique_ptrstd::shared_ptr功能一样,除了std::unique_ptr是独占资源所有权。意思是std::unique_ptr对象不能与其他的std::unique_ptr对象共享资源。std::unique_ptr也接受一个裸指针来创建对象。

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
#include <iostream>
#include <memory>

struct Task
{
int mId;
Task(int id ) :mId(id)
{
std::cout<<"Task::Constructor"<<std::endl;
}
~Task()
{
std::cout<<"Task::Destructor"<<std::endl;
}
};

int main()
{
// Create a unique_ptr object through raw pointer
std::unique_ptr<Task> taskPtr(new Task(23));

//Access the element through unique_ptr
int id = taskPtr->mId;

std::cout<<id<<std::endl;


return 0;
}

输出:

1
2
3
Task::Constructor
23
Task::Destructor

std::unique_ptr对象taskPtr接收一个裸指针作为参数。当函数退出时,taskPtr对象会超出作用域,析构函数会被调用。在析构函数中,taskPtr会删除与之相关的裸指针。

不管函数是正常退出还是异常退出,taskPtr的析构函数都会被调用。因此不会造成内存泄漏的情况。

unique指针的独享所有权

独享所有权的意思是无法拷贝std::unique_ptr对象。只能move。每一个std::unique_ptr对象都只是裸指针的唯一拥有者,所有std::unique_ptr对象内部无需管理引用计数。

创建一个空的std::unique_ptr对象

1
2
// 空的`std::unique_ptr`对象
std::unique_ptr<int> ptr1;

因为没有裸指针与之关联,所以ptr1是空的。

检查std::unique_ptr对象是否为空

方法1:

1
2
if(!ptr1)
std::cout<<"ptr1 is empty"<<std::endl;

方法2:

1
2
if(ptr1 == nullptr)
std::cout<<"ptr1 is empty"<<std::endl;

使用裸指针创建std::unique_ptr对象

1
std::unique_ptr<Task> taskPtr(new Task(23));

不能通过赋值的方式来创建std::unique_ptr对象。

1
std::unique_ptr<Task> taskPtr2 = new Task(); // Compile Error

重置std::unique_ptr对象

调用reset()函数会导致std::unique_ptr对象被重置,也就是与之关联的裸指针会被删除,同时std::unique_ptr对象会被置空。

1
taskPtr.reset();

std::unique_ptr对象无法被拷贝

1
2
3
4
5
6
7
8
// 创建 `unique_ptr` 对象
std::unique_ptr<Task> taskPtr2(new Task(55));

// 编译错误
std::unique_ptr<Task> taskPtr3 = taskPtr2;

// 编译错误
taskPtr = taskPtr2;

转移std::unique_ptr对象所有权

无法拷贝std::unique_ptr对象,但是可以转移std::unique_ptr对象的所有权。

1
2
3
4
5
6
7
8
9
10
11
12
std::unique_ptr<Task> taskPtr2(new Task(55));

std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);

if(taskPtr2 == nullptr)
std::cout<<"taskPtr2 is empty"<<std::endl;

// `taskPtr2`的所有权被转移到 `taskPtr4`对象
if(taskPtr4 != nullptr)
std::cout<<"taskPtr4 is not empty"<<std::endl;

std::cout<<taskPtr4->mId<<std::endl;

taskPtr2对象在转移所有权后会被置为空。

释放裸指针

通过调用release()函数,可以从std::unique_ptr对象中将裸指针释放出来,它返回一个裸指针。

1
2
3
4
5
6
7
8
9
std::unique_ptr<Task> taskPtr5(new Task(55));

if(taskPtr5 != nullptr)
std::cout<<"taskPtr5 is not empty"<<std::endl;

Task * ptr = taskPtr5.release();

if(taskPtr5 == nullptr)
std::cout<<"taskPtr5 is empty"<<std::endl;

完整例子:

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <iostream>
#include <memory>

struct Task
{
int mId;
Task(int id ) :mId(id)
{
std::cout<<"Task::Constructor"<<std::endl;
}
~Task()
{
std::cout<<"Task::Destructor"<<std::endl;
}
};

int main()
{
// Empty unique_ptr object
std::unique_ptr<int> ptr1;

// Check if unique pointer object is empty
if(!ptr1)
std::cout<<"ptr1 is empty"<<std::endl;

// Check if unique pointer object is empty
if(ptr1 == nullptr)
std::cout<<"ptr1 is empty"<<std::endl;

// can not create unique_ptr object by initializing through assignment
// std::unique_ptr<Task> taskPtr2 = new Task(); // Compile Error

// Create a unique_ptr object through raw pointer
std::unique_ptr<Task> taskPtr(new Task(23));

// Check if taskPtr is empty or it has an associated raw pointer
if(taskPtr != nullptr)
std::cout<<"taskPtr is not empty"<<std::endl;

//Access the element through unique_ptr
std::cout<<taskPtr->mId<<std::endl;

std::cout<<"Reset the taskPtr"<<std::endl;
// Reseting the unique_ptr will delete the associated
// raw pointer and make unique_ptr object empty
taskPtr.reset();

// Check if taskPtr is empty or it has an associated raw pointer
if(taskPtr == nullptr)
std::cout<<"taskPtr is empty"<<std::endl;


// Create a unique_ptr object through raw pointer
std::unique_ptr<Task> taskPtr2(new Task(55));

if(taskPtr2 != nullptr)
std::cout<<"taskPtr2 is not empty"<<std::endl;

// unique_ptr object is Not copyable
//taskPtr = taskPtr2; //compile error

// unique_ptr object is Not copyable
//std::unique_ptr<Task> taskPtr3 = taskPtr2;

{
// Transfer the ownership

std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);


if(taskPtr2 == nullptr)
std::cout<<"taskPtr2 is empty"<<std::endl;

// ownership of taskPtr2 is transfered to taskPtr4
if(taskPtr4 != nullptr)
std::cout<<"taskPtr4 is not empty"<<std::endl;

std::cout<<taskPtr4->mId<<std::endl;

//taskPtr4 goes out of scope and deletes the assocaited raw pointer
}

// Create a unique_ptr object through raw pointer
std::unique_ptr<Task> taskPtr5(new Task(55));

if(taskPtr5 != nullptr)
std::cout<<"taskPtr5 is not empty"<<std::endl;

// Release the ownership of object from raw pointer
Task * ptr = taskPtr5.release();

if(taskPtr5 == nullptr)
std::cout<<"taskPtr5 is empty"<<std::endl;

std::cout<<ptr->mId<<std::endl;

delete ptr;

return 0;
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ptr1 is empty
ptr1 is empty
Task::Constructor
taskPtr is not empty
23
Reset the taskPtr
Task::Destructor
taskPtr is empty
Task::Constructor
taskPtr2 is not empty
taskPtr2 is empty
taskPtr4 is not empty
55
Task::Destructor
Task::Constructor
taskPtr5 is not empty
taskPtr5 is empty
55
Task::Destructor
页阅读量:  ・  站访问量:  ・  站访客数: