智能指针
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
