一、为什么需要智能指针
- 智能指针主要解决以下问题:
- 内存泄漏:内存需要手动释放,使用智能指针可以自动释放。
- 共享所有权指针的传播和释放,比如多线程使用同一个对象时析构问题。
- C++ 里面的四个智能指针:
auto_ptr、shared_ptr
、unique_ptr
、weak_ptr
,其中后三个是 C++ 11支持,并且第一个已经被 C++ 11 弃用。shared_ptr
共享对象的所有权,但性能略差。unique_ptr
独占对象的所有权,由于没有引用计数,因此性能较好。weak_ptr
配合shared_ptr
,解决循环引用的问题。
二、shared_ptr 内存模型
shared_ptr
内部包含两个指针:一个指向对象,另一个指向控制块(control block),控制块中包含一个引用计数(reference count),一个弱计数(weak count) 和其它的一些数据 。
- 自动释放内存
- 智能指针退出作用域,在栈上自动析构。
buf2
先析构,reference_count = 1
,buf
再析构,reference_count = 0
,释放对象T Object
。{ shared_ptr<Buffer> buf = make_shared<Buffer>("auto free memory"); buf2 = buf; }
三、shared_ptr 共享的智能指针
std::shared_ptr
使用引用计数,每一个shared_ptr
的拷贝都指向相同的内存,在最后一个shared_ptr
析构的时候,内存才会被释放。shared_ptr
共享被管理的对象,同一时刻可以有多个shared_ptr
拥有对象的所有权,当最后一个shared_ptr
对象销毁时,被管理对象自动销毁。shared_ptr
实现包含了两部分:- 一个指向堆上创建的对象的裸指针,
raw_ptr
。 - 一个指向内部隐藏的,共享的管理对象,
share_count_object
。use_count
:当前这个堆上对象被多少对象引用了,也就是引用计数。
- 一个指向堆上创建的对象的裸指针,
四、shared_ptr 基本用法
get()
std::shared_ptr<int> s(new int(1)); int *p = s.get(); // 返回 shared_ptr 中保存的裸指针 /* 谨慎使用 s.get() 的返回值,遵循以下约定 不要保存 p.get() 的返回值,无论是保存为裸指针还是 shared_ptr 都是错误的 保存为裸指针不知什么时候就会变成空悬指针,保存为 shared_ptr 则产生了独立指针 不要 delete p.get() 的返回值,会导致对一块内存 delete 两次的错误 */
reset()
s.reset(...); // 重置 shared_ptr /* reset() 不带参数时: 若智能指针 s 是唯一指向该对象的指针,则释放,并置空。 若智能指针 s 不是唯一指向该对象的指针,则引用计数减少 1, 同时将 s 置空 reset() 带参数时: 若智能指针 s 是唯一指向该对象的指针,则释放并指向新的对象。 若智能指针 s 不是唯一指向该对象的指针,则只减少引用计数,并指向新的对象 */ auto s = make_shared<int>(100); s.reset(new int(200));
use_count()
和unique()
s.use_count(); // 返回 shared_ptr 的强引用计数 s.unique(); // 若 use_count 为 1 ,返回 true,否则返回 false
- 优先使用
make_shared
来构造智能指针,因为更高效。// 初始化 shared_ptr std::shared_ptr<int> p1(new int(1)); std::shared_ptr<int> p2 = p1; std::shared_ptr<int> p3; p3.reset(new int(1)); auto sp = make_shared<int>(100); std::shared_ptr sp1 = make_shared<int>(100);
- 智能指针可通过重载的 bool 类型操作符来判断。
std::shared_ptr<int> p1; p1.reset(