c++11智能指针
c++11中引入了3个智能指针类型:
- std::unique_ptr<T> :独占资源所有权的指针
- std::shared_ptr<T> :共享资源所有权的指针
- std::weak_ptr<T> : 共享资源的观察者,需要和std::shared_ptr一起使用
std::unique_ptr
当我们独占资源的所有权的时候,可以使用 std::unique_ptr 对资源进行管理——离开 unique_ptr 对象的作用域时,会自动释放资源。这是很基本的 RAII 思想。
需要注意的是:std::unique_ptr是move-only的
{ std::unique_ptr<int> uptr = std::make_unique<int>(200); std::unique_ptr<int> uptr1 = uptr; // 编译错误,std::unique_ptr<T> 是 move-only 的 std::unique_ptr<int> uptr2 = std::move(uptr); assert(uptr == nullptr); }
std::unique_ptr可以指向一个数组以及自定义deleter
std::shared_ptr
对引用资源进做引用计数,当引用计数为0的时候,自动释放资源。
{ std::shared_ptr<int> sptr = std::make_shared<int>(200); assert(sptr.use_count() == 1); // 此时引用计数为 1 { std::shared_ptr<int> sptr1 = sptr; assert(sptr.get() == sptr1.get()); assert(sptr.use_count() == 2); // sptr 和 sptr1 共享资源,引用计数为 2 } assert(sptr.use_count() == 1); // sptr1 已经释放 } // use_count 为 0 时自动释放内存
std::shared_ptr也可以指向一个数组(c++20)以及自定义deleter
std::weak_ptr
std::weak_ptr 要与 std::shared_ptr 一起使用。 一个 std::weak_ptr 对象看做是 std::shared_ptr 对象管理的资源的观察者,它不影响共享资源的生命周期:
- 如果需要使用 weak_ptr 正在观察的资源,可以将 weak_ptr 提升为 shared_ptr。
- 当 shared_ptr 管理的资源被释放时,weak_ptr 会自动变成 nullptr。
void Observe(std::weak_ptr<int> wptr) { if (auto sptr = wptr.lock()) { std::cout << "value: " << *sptr << std::endl; } else { std::cout << "wptr lock fail" << std::endl; } } std::weak_ptr<int> wptr; { auto sptr = std::make_shared<int>(111); wptr = sptr; Observe(wptr); // sptr 指向的资源没被释放,wptr 可以成功提升为 shared_ptr } Observe(wptr); // sptr 指向的资源已被释放,wptr 无法提升为 shared_ptr
作为资源的观察者,内部应该有一些比较实用的功能,但是自己并没有使用过。至于为什么记录这个智能指针,是因为自己的一个需求间接的使用到了这个。
一个很常见的功能,就是类的成员函数内部获取指向自身(this)的 shared_ptr,看看下面这个例子有没有问题
class Foo { public: std::shared_ptr<Foo> GetSPtr() { return std::shared_ptr<Foo>(this); } }; auto sptr1 = std::make_shared<Foo>(); assert(sptr1.use_count() == 1); auto sptr2 = sptr1->GetSPtr(); assert(sptr1.use_count() == 1); assert(sptr2.use_count() == 1);
上面的代码其实会生成两个独立的 shared_ptr,他们的控制块是独立的,最终导致一个 Foo 对象会被 delete 两次。第二次会报错。
成员函数获取 this 的 shared_ptr 的正确的做法是继承 std::enable_shared_from_this。
class Bar : public std::enable_shared_from_this<Bar> { public: std::shared_ptr<Bar> GetSPtr() { return shared_from_this(); } }; auto sptr1 = std::make_shared<Bar>(); assert(sptr1.use_count() == 1); auto sptr2 = sptr1->GetSPtr(); assert(sptr1.use_count() == 2); assert(sptr2.use_count() == 2);
一般情况下,继承了 std::enable_shared_from_this 的子类,成员变量中增加了一个指向 this 的 weak_ptr。这个 weak_ptr 在第一次创建 shared_ptr 的时候会被初始化,指向 this。
但是似乎继承了 std::enable_shared_from_this 的类都被强制必须通过 shared_ptr 进行管理。
auto b = new Bar; auto sptr = b->shared_from_this();
智能指针,本质上是对资源所有权和生命周期管理的抽象:
- 当资源是被独占时,使用 std::unique_ptr 对资源进行管理。
- 当资源会被共享时,使用 std::shared_ptr 对资源进行管理。
- 使用 std::weak_ptr 作为 std::shared_ptr 管理对象的观察者。
- 通过继承 std::enable_shared_from_this 来获取 this 的 std::shared_ptr 对象。