C++ 11 shared_ptr 与 weak_ptr

本文详细介绍了C++中的智能指针std::shared_ptr和std::weak_ptr,包括它们的创建、操作、错误用法以及std::weak_ptr的作用,特别强调了循环引用问题的解决和观察者模式的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简单记录一下智能指针的 API

一 std::shared_ptr

共享智能指针,通过 引用计数RAII 机制,实现多个共享智能指针对同一资源(对象)的使用和自动管理,自动进行资源(对象)的释放,无需程序员手动管理

下列情况之一出现时销毁对象释放其内存:

  • 最后剩下的占有对象的 shared_ptr 被销毁(析构);
  • 最后剩下的占有对象的 shared_ptr 被通过 operator = 或 reset( ) 赋值为另一指针。
头文件 #include <memory>
template< class T > class shared_ptr;

1. 创建 shared_ptr

构造函数

#include <memory>

// 默认构造一个空的 shared_ptr
std::shared_ptr<int> ptr;

// 创建一个 shared_ptr 对象,管理一个 int 类型的资源
std::shared_ptr<int> ptr(new int(42));

// 创建一个 shared_ptr 对象,管理一个自定义类型的资源
std::shared_ptr<MyClass> ptr(new MyClass());

std::shared_ptr<int> originalPtr(new int(42));
// 使用复制构造函数创建一个新的 shared_ptr 对象
std::shared_ptr<int> copiedPtr = originalPtr;
// 使用移动构造函数创建一个新的 shared_ptr 对象
std::shared_ptr<int> movedPtr = std::move(originalPtr);

使用 std::make_shared

// 创建一个 shared_ptr 对象,管理一个 int 类型的资源
std::shared_ptr<int> ptr1 = std::make_shared<int>(42);

// 创建一个 shared_ptr 对象,管理一个自定义类型的资源
std::shared_ptr<MyClass> ptr2 = std::make_shared<MyClass>();

可以指定一个删除器


std::shared_ptr<Foo> ptr(new Foo, [](Foo *p) {
   std::cout << "Call delete from lambda...\n";
   delete p;
});

可以实现一些自定义的功能,在引用计数为 0 时,可以选择不 delete,实现其他功能,比如归还享元池

链接 C++实现数据库连接池

错误用法

int *p = new int(1);
std::shared_ptr<int> sp1(p);
std::shared_ptr<int> sp2(p);
// 导致重复释放,sp1 相关的引用计数和 sp2 的没有联系

2. 操作

shared_ptr 对象本身的方法

void swap( shared_ptr& r ) noexcept;
// 交换 *this 与 r 的存储指针值与所有权。不调整引用计数,若它们存在

void reset() noexcept;
// 释放被管理对象的所有权,若存在。调用后, *this 不再管理对象。等价于 shared_ptr().swap(*this);
// 引用计数-1

template< class Y >
void reset( Y* ptr );
template< class Y, class Deleter >
void reset( Y* ptr, Deleter d );
// 使用与构造类似,重置而管理另一个对象
long use_count() const noexcept;
// 返回管理当前对象的不同 shared_ptr 实例数量。若无管理对象,则返回 0 

bool unique() const noexcept;
// 是否有 use_count() == 1

T* get() const noexcept;
// 返回管理的指针

explicit operator bool() const noexcept;
// 可以用于条件判断中,即可以直接 if (ptr),像普通指针一样
// 判断是否有 get() != nullptr

可以像普通指针一样使用对象的方法

T& operator*() const noexcept;
T* operator->() const noexcept;

auto ptr = std::make_shared<Foo>(10);
ptr->print();
(*ptr).print();

3. 作为函数参数

对小白来说,在 C++ 里函数参数和返回问题确实是一个头疼的问题,这里简单的说明一下

1.作为值传递

void func(std::shared_ptr<Data> p) {
}

函数参数是通过值传递的,会创建传入 shared_ptr 的一个副本,这意味着在函数内部,有一个新的 shared_ptr 对象,它与传入的 shared_ptr 共享相同的对象,并且引用计数会相应地 +1,函数返回后 -1。当然,在函数内部重新赋值或使用 reset 重置 p,它不会影响传入的 shared_ptr

2.作为引用传递

void func(std::shared_ptr<Data>& p) {
}

函数参数是通过引用传递的,可以在函数内部修改传入的 shared_ptr 对象,可以改变 p 所引用的对象,或者重置它为 nullptr。当然,引用计数不会受到影响,因为是引用,没有拷贝发生,在函数内部可以将 p 这个标识符直接当做传入的 shared_ptr,对 p 的任何操作都将解释为对原来实参的操作

如果是常量引用 const reference,那么只能读,不能写

链接 shared_ptr 底层实现原理

二 std::weak_ptr

头文件 #include <memory>
template< class T > class weak_ptr;

weak_ptr 是被设计用来解决 shared_ptr 的循环引用问题的

它对被 shared_ptr 管理的对象持有非拥有性的弱引用。在访问所引用的对象前必须先转换为 std::shared_ptr

1. 创建 weak_ptr

weak_ptr 对象通过 shared_ptr 对象或拷贝移动来创建,不能通过裸指针

构造函数

#include <memory>

// 创建一个空 weak_ptr
std::weak_ptr<Foo> w_ptr;

// 创建一个 shared_ptr 对象
std::shared_ptr<int> sharedPtr(new int(42));
// 创建一个 weak_ptr 对象,观测 sharedPtr 所管理的资源
std::weak_ptr<int> weakPtr(sharedPtr);

// weak_ptr 同样也支持拷贝和移动

weak_ptr 只是作为一个旁观者,不影响引用计数。对于编译器具体的实现,可能表现为引用计数不增加,增加弱引用计数,即有两种引用计数,所以 weak_ptr 是为了解决 shared_ptr 的问题,需要通过 shared_ptr 来创建

2. 操作

void reset() noexcept;
// 释放被管理对象的所有权。调用后 *this 不管理对象

void swap( weak_ptr& r ) noexcept;
// 交换 *this 与 r 的存储指针值与所有权。不调整引用计数,若它们存在

long use_count() const noexcept;
// 返回共享被管理对象所有权的 shared_ptr 实例数量

bool expired() const noexcept;
// use_count() == 0

std::shared_ptr<T> lock() const noexcept;
// expired() ? shared_ptr<T>() : shared_ptr<T>(*this)

weak_ptr 不能直接访问底层资源,但是它知道这个资源还在不在。使用 weak_ptr 主要就是通过这个 lock() 方法来转换成 shared_ptr,再次强调在访问所引用的对象前必须先转换为 std::shared_ptr,即不能通过这个 weak_ptr 来使用对象

3. 为什么还要 weak_ptr

weak_ptr MSVC实现原理、解决 std::shared_ptr 循环引用计数问题

链接:从源码看std::weak_ptr

解决观察者模式观察者是否存在的问题

// 来自陈硕 <<Linux多线程服务端编程:使用muduo C++网络库>>

class Observable { // 还不是完全线程安全
public:
    void register(weak_ptr<Observer> observer); // 参数也可以是 const &
    // void unregister() 不需要
    void notifyObservers();
    
private:
    mutable MutexLock mutex_;
    std::vector<weak_ptr<Observer>> observers_;
    typedef std::vector<weak_ptr<Observer>>::iterator Iterator;
};

void Observable::notifyObservers() {
    MutexLockGuard lock(mutex_);
    Iterator it = observers_.begin();
    while (it != observers_.end()) {
        share_ptr<Observer> observer(it->lock());// 通过 weak_ptr 获得 shared_ptr
        if (observer) {
            observer->update();
            it++;
        }else {
            it = observers_.erase(it);// 观察者已失效,从容器中删除
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值