智能指针
Nevermore 2023-01-13 Lang
需要解决的问题
- 定义一个能够像普通指针一样使用的指针
- 作用域结束后自动释放管理的资源
# auto_ptr
在给指针赋值或拷贝构造时,原来的ptr指向nullptr来保证资源不会被释放两次, 但是造成原指针的失效。
#include <iostream>
#include <memory>
int main()
{
std::auto_ptr<int> pn(new int);
*pn = 100;
std::auto_ptr<int> pb(pn);
*pn = 200;
std::cout << *pb << std::endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# unique_ptr
直接将拷贝构造和赋值delete或者声明成私有,防止拷贝。
# shared_ptr
- 只引用计数是线程安全的,指向的资源需要自己实现线程安全。
#include <iostream>
#include <memory>
int main()
{
std::shared_ptr<int> pa(new int);
std::shared_ptr<int> pb(pa);
std::shared_ptr<int> pc(pb);
*pc = 200;
std::cout << *pa << std::endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- 模拟实现
template <typename T>
class shared_ptr{
private:
void Realease() {
m_mtx->lock();
bool flag = false;
if(--(*m_ref) == 0 && m_ptr) {
delete m_ptr;
delete m_ref;
m_ptr = nullptr;
m_ref = nullptr;
flag = true;
}
m_mtx->unlock();
if(flag && m_mtx != nullptr) {
delete m_mtx;
m_mtx = nullptr;
}
}
void AddRef() {
m_mtx->lock();
*m_ref++;
m_mtx->unlock();
}
public:
shared_ptr(T* ptr) :m_ptr(ptr), m_ref(new int(1)), m_mtx(new std::mutex) {}
shared_ptr(shared_ptr<T> &sp) {
m_ptr= sp.m_ptr;
m_ref = sp.m_ref;
m_mtx = sp.m_mtx;
AddRef();
}
shared_ptr<T>& operator=(const shared_ptr<T> &sp) { //当前指针去管理其他资源
if(this->m_ptr != &sp.m_ptr) {
Realease();
m_ptr = sp.m_ptr;
m_ref = sp.m_ref;
m_mtx = sp.m_mtx;
AddRef();
}
return *this;
}
~shared_ptr() {
Realease();
}
T& operator*() {
return *m_ptr;
}
T* operator->() {
return m_ptr;
}
private:
T* m_ptr;
int *m_ref; //指向堆,引用计数
std::mutex* m_mtx;
};
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
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
# weak_ptr
- 不管理具体的指针对象,可以使用share_ptr对weak_ptr赋值
- 使用share_ptr存在循环引用的问题,以下程序无法看到资源的析构
struct ListNode{
std::shared_ptr<ListNode> pre;
std::shared_ptr<ListNode> next;
int val;
~ListNode() {
std::cout << "~ListNode()" << std::endl;
}
};
int main()
{
std::shared_ptr<ListNode> p1(new ListNode);
std::shared_ptr<ListNode> p2(new ListNode);
p1->next = p2;
p2->pre = p1;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 使用weak_ptr修改ListNode结构体运行可以看到函数的析构,因为其指向对象但不增加引用计数
struct ListNode{
std::weak_ptr<ListNode> pre;
std::weak_ptr<ListNode> next;
int val;
~ListNode() {
std::cout << "~ListNode()" << std::endl;
}
};
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 其他
- 删除数组,需要其他类型的删除方式
class A {
public:
~A() {
std::cout << "~A()" << std::endl;
}
};
template<typename T>
class DeleteArr {
public:
void operator() (const T* ptr) {
delete[] ptr;
}
};
int main()
{
// 智能指针默认析构delete一次,对于初始化的数组,需要定制删除器
std::unique_ptr<A,DeleteArr<A>> sp(new A[10]);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 关闭文件
class DeleteArr {
public:
void operator() (FILE* ptr) {
fclose(ptr);
}
};
int main()
{
// 智能指针默认析构delete一次,对于初始化的数组,需要定制删除器
std::unique_ptr<FILE,DeleteArr> sp(fopen("1.txt", "w"));
std::shared_ptr<A> sp2(new A[10], DeleteArr<A>()); // shared_ptr传入删除器方式不同
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13