智能指针是利用了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
主要用来避免多次内存分配,直接原地构造智能指针和对象,避免先为了对象分配内存,之后再为智能指针分配内存。
叨叨几句... NOTHING