简单记录一下智能指针的 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,那么只能读,不能写
二 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 循环引用计数问题
解决观察者模式观察者是否存在的问题
// 来自陈硕 <<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);// 观察者已失效,从容器中删除
}
}
}