一、RAII
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内
存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在
对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做
法有两大好处:
- 不需要显式地释放资源。
- 采用这种方式,对象所需的资源在其生命期内始终保持有效。
简单来说,智能指针是RAII思想的一种实现。
二、auto_ptr
C++98版本的库中就提供了auto_ptr的智能指针。
auto_ptr的实现原理:转移管理权,使得最后一个拷贝对象管理资源,被拷贝对象都被置空。
基础实现:
template<class T>
class auto_ptr
{
public:
auto_ptr(T* ptr)
:_ptr(ptr)
{}
~auto_ptr()
{
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
导致问题:
void autoptr_test()
{
autoptr::auto_ptr<int> p(new int);
autoptr::auto_ptr<int> p2 = p;
}
析构两次,程序崩溃。
解决:
template<class T>
class auto_ptr
{
public:
auto_ptr(T* ptr)
:_ptr(ptr)
{}
~auto_ptr()
{
if (_ptr)
{
delete _ptr;
_ptr = nullptr;
}
}
//转移管理权
auto_ptr(auto_ptr<T>& ap)
:_ptr(ap._ptr)
{
ap._ptr = nullptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
新问题:
管理权转移,导致对象悬空。
可能会访问置为nullptr的_ptr。
因此auto_ptr使用不多。
三、unique_ptr
实现原理:防拷贝
template<class T>
class unique_ptr
{
public:
unique_ptr(T* ptr)
:_ptr(ptr)
{}
~unique_ptr()
{
if (_ptr)
{
delete _ptr;
_ptr = nullptr;
}
}
unique_ptr(const unique_ptr<T>& up) = delete;
unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
四、shared_ptr
原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。
template<class T>
class shared_ptr
{
public:
shared_ptr(T* ptr = T())
:_ptr(ptr)
{
_pcount = new int;
*_pcount = 1;
}
shared_ptr(shared_ptr<T>& sp)
:_ptr(sp._ptr)
,_pcount(sp._pcount)
{
(*_pcount)++;
}
shared_ptr<T>& operator=(shared_ptr<T>& sp)
{
if (_ptr != sp._ptr)
{
if (--(*_pcount) == 0)
{
delete _ptr;
_ptr = nullptr;
delete _pcount;
}
_ptr = sp._ptr;
(*sp._pcount)++;
_pcount = sp._pcount;
}
return *this;
}
~shared_ptr()
{
if (--(*_pcount) == 0)
{
delete _ptr;
_ptr = nullptr;
delete _pcount;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
int* _pcount;
};
问题:循环引用
struct ListNode
{
int _val;
sharedptr::shared_ptr<ListNode> _prev;
sharedptr::shared_ptr<ListNode> _next;
~ListNode()
{
cout << "~ListNode" << endl;
}
};
void sharedptr_test2()
{
sharedptr::shared_ptr<ListNode> n1 = new ListNode;
sharedptr::shared_ptr<ListNode> n2 = new ListNode;
n1->_next = n2;
n2->_prev = n1;
}
当程序语句执行完毕,开始析构时,n2先调用析构函数,则n2的*_pcount变为1,未释放空间。
然后n1再调用析构函数,n1的*_pcount 变为1,未释放空间。
此时如果想要释放n2
=> 需要释放(析构)n1中的_next,使得n2的*_pcount变为0
=>需要释放(析构)n1,从而析构n1中的成员_next
=>需要释放(析构)n2中的_prev,使得n1的*_pcount变为0
=>需要释放(析构)n2,从而析构n2中的成员_prev
=>形成闭环
解决:使用weak_ptr
相当于ListNode中的成员_prev和_next不参与管理,引用计数不会+1
struct ListNode
{
int _val;
std::weak_ptr<ListNode> _prev;
std::weak_ptr<ListNode> _next;
~ListNode()
{
cout << "~ListNode" << endl;
}
};
void sharedptr_test2()
{
std::shared_ptr<ListNode> n1(new ListNode);
std::shared_ptr<ListNode> n2(new ListNode);
n1->_next = n2;
n2->_prev = n1;
}
定制删除器
void sharedptr_test3()
{
std::shared_ptr<set<int>> p1(new set<int>[10], [](set<int>* ptr) {delete[] ptr; });
auto p2 = p1;
std::shared_ptr<ListNode> p3(new ListNode[10], [](ListNode* ptr) {delete[] ptr; });
}
模拟实现:
template<class T>
class shared_ptr
{
public:
shared_ptr(T* ptr = nullptr)
:_ptr(ptr)
{
_pcount = new int;
*_pcount = 1;
}
template<class D>
shared_ptr(T* ptr, D del)
:_ptr(ptr)
, _pcount(new int(1))
, _del(del)
{}
shared_ptr(shared_ptr<T>& sp)
:_ptr(sp._ptr)
,_pcount(sp._pcount)
,_del(sp._del)
{
(*_pcount)++;
}
void release()
{
_del(_ptr);
delete _pcount;
}
shared_ptr<T>& operator=(shared_ptr<T>& sp)
{
if (_ptr != sp._ptr)
{
if (--(*_pcount) == 0)
{
release();
}
_ptr = sp._ptr;
(*sp._pcount)++;
_pcount = sp._pcount;
_del = sp._del;
}
return *this;
}
~shared_ptr()
{
if (--(*_pcount) == 0)
{
release();
}
}
int use_count() const
{
return *_pcount;
}
T* get() const
{
return _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
int* _pcount;
function<void(T*)> _del = [](T* _ptr) { delete _ptr; };
};