c++智能指针

发布于 2021-09-18  40 次阅读


智能指针是利用了RAII(资源获取即初始化)的技术对普通的指针进行封装。作用是帮助管理对象的声明周期,令资源得以自动释放。

c++一共提供了三种智能指针->unique_ptr,shared_ptr,weak_ptr。此外,使用智能指针并不意味着抛弃裸指针,在多种场景下需要结合使用。

unique_ptr

这是表达唯一所有权语义的智能指针,同一个对象同时只能有一个 unique_ptr 指向它。 unique_ptr 不支持拷贝,只支持移动。

所以在实际编程中,通常结合裸指针来简化代码。在某些仅表达引用语义,并非所有权的地方使用裸指针。

unique_ptr 是非线程安全的,实际上根本不会在多线程中共享一个 unique_ptr。这里的共享可能指的是这样的操作:

int main()
{
    unique_ptr<int> work;

    thread t1([&] {
        while (true) {
            const unique_ptr<int> localWork = move(work);
            if (localWork)
                printf("thread1: %d\n", *localWork);
            this_thread::yield();
        }
    });

    thread t2([&] {
        while (true) {
            const unique_ptr<int> localWork = move(work);
            if (localWork)
                printf("thread2: %d\n", *localWork);
            this_thread::yield();
        }
    });

    for (int i = 0; ; i++) {
        work.reset(new int(i));

        while (work)
            this_thread::yield();
    }

    return 0;
}

在两个线程中同时转移一个unique_ptr 的所有权,可能会出现两个线程均获取到了指针,或均获取到了nullptr的情况。

shared_ptr

shared_ptr 在内部使用一个引用计数来管理对象的生命周期,通常用于某个对象需要在多个线程里共享的场景。

标准里提到, shared_ptr 的引用计数本身是原子的,是线程安全的,但 shared_ptr 整体并不是线程安全的。shared_ptr内部结构大概长这样:

class shared_ptr {
 atomic_Ref_count * count; //指向引用计数块
 atomic_element_ptr * ptr; 
}

其具有两个成员变量。当复制发生的时候,首先设置指针,之后递增引用计数。但可能会出现设置指针后,其他线程将引用计数清零了,导致对象内存被析构。新创建的 shared_ptr 指向了一个已经释放了的内存。

所以在多线程中对同一个 shared_ptr 进行修改操作(析构的时候是不安全的。

此外, shared_ptr 的创建必须经过复制构造,否则其使用的引用计数不是同一个:

int * n;
std::shared_ptr<int> p1(n)
std::shared_ptr<int> p2(n)

以上代码会创建指向同一个对象的不同智能指针,其引用计数均为1。

weak_ptr

weak_ptr 是用来解决 shared_ptr 的循环引用问题。其不表达所有权,只是一个引用,并且不增加引用计数。当实际使用的时候需要”锁定“,去检查它所指向的 shared_ptr 是否还存在。

底层是通过一个指针指向shared_ptr的共享引用计数区域,通过检查引用计数来判断的。

make_shared/make_unique

主要用来避免多次内存分配,直接原地构造智能指针和对象,避免先为了对象分配内存,之后再为智能指针分配内存。


当其他人都认为你要鸽的时候,你鸽了,亦是一种不鸽