1.1 share_ptr
引用计数型智能指针
作用:解决内存泄露问题,管理共享资源
三种初始化方式:make_shared、new初始化(不能赋值)、另一个shared_ptr初始化。
使用shared_ptr注意事项:
- 尽量不要使用相同的原始指针来创建多个shared_ptr对象,因为在这种情况下,不同的shared_ptr对象不会知道它们与其他shared_ptr对象共享指针。
int *rawPtr = new int();
std::shared_ptr<int> ptr_1(rawPtr);
std::shared_ptr<int> ptr_2(rawPtr);
- 不要从栈而不是堆的内存中创建shared_ptr对象
#include <iostream>
#include <memory>
int main() {
int x = 12;
std::shared_ptr<int> ptr(&x);
return 0;
}
1.2 shared_ptr模拟实现
template <T>
class SharedPtr {
public:
SharedPtr(T* t):_ptr(t),_count(new int(1)) {}
SharedPtr(const SharedPtr& sp):_ptr(sp._ptr),_count(sp._count){}
SharedPtr<T>& operator=(const SharedPtr<T>& sp) {
if(this != sp) {
if(--(*_count) == 0) {
delete _count;
delete _ptr;
}
_ptr = sp._ptr;
_count = sp._count;
(*_count)++;
}
return *this;
}
~SharedPtr() {
if(--(*_count) == 0){
delete _count;
delete _ptr;
}
else{
(*_count)--;
}
}
T& operator*() {
return *_ptr;
}
T* operator->() {
return _ptr;
}
private:
T* _ptr;
int* _count;
}
1.3 shared_ptr循环引用
考虑双向链表的例子,pre和next指针都是shared_ptr
示例:
template<class T>
class SharedPtr;
template<class T>
struct ListNode
{
T _data;
SharedPtr<ListNode<T>> _next;
SharedPtr<ListNode<T>> _prev;
ListNode(T data)
:_data(data)
, _next(NULL)
, _prev(NULL) {}
};
//中间部分与上面SharedPtr代码一样
void TestSharedPtr()
{
SharedPtr<ListNode<int>> sp1(new ListNode<int> (10));
SharedPtr<ListNode<int>> sp2(new ListNode<int>(20));
sp1->_next = sp2;
sp2->_prev = sp1;
}
分析:
- 有两个share_ptr对象sp1和sp2,刚开始sp1和sp2的引用计数都为1;
- 将sp2赋值给sp1->_next的时候,sp1->_next的引用计数变为2且sp2的引用计数也变为2;将sp1赋值给sp2->_prev时,sp2->_prev的引用计数变为2且sp1的引用计数也变为2。
- 又因为sp1里的_next和_prev两个指针的释放依赖于sp1,而sp1的释放又依赖于sp2->_prev,sp2的_prev的释放又依赖于sp2,sp2又依赖于sp1的_next的释放,这就是我的释放依赖于你,你的释放依赖于我,就会导致陷入死循环中,导致空间没有被释放,进而就会产生内存泄露的问题。
更一般的情况是owner持有child的shared_ptr,而child又持有owner的shared_ptr,由此形成了循环引用,解决方法是让child持有owner的weak_ptr
2.weak_ptr
没有引用计数,一般用于解决循环引用问题。
weak_ptr本身也是一个模板类,但是不能直接用它来定义一个智能指针的对象,只能配合shared_ptr来使用,可以将shared_ptr的对象赋值给weak_ptr,并且这样并不会改变引用计数的值。
weak_ptr中只有函数lock和expired两个函数比较重要,因为它本身不会增加引用计数,所以它指向的对象可能在它用的时候已经被释放了,所以在用之前需要使用expired函数来检测是否过期,然后使用lock函数来获取其对应的shared_ptr对象。
3.unique_ptr
一个unique_ptr"拥有“他所指向的对象。与shared_ptr不同,某个时刻只能有一个unique_ptr指向一个给定的对象。当unique_ptr被销毁时,它所指向的对象也被销毁。uniptr_ptr表达的是一种独占的思想。