在某些场景下,比如在抛异常的场景下,如果我们有一个指针在抛异常之前,而指针的释放在抛异常之后的话,当出现异常后,编译器会直接跳到捕获异常处,从而这个指针就不会被释放,从而造成内存泄漏的问题。
C++98版本的库中就提供了auto_ptr的智能指针。auto_ptr的实现原理:管理权转移的思想。
#pragma once
#include
#include
#include
#include
using namespace std;
namespace L
{
template
class AutoPtr
{
public:
AutoPtr(T* ptr)
:_ptr(ptr)
{}
~AutoPtr()
{
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
auto_ptr通俗来说是浅拷贝,会导致两个指针同时指向同一资源,导致被拷贝对象悬空,因此很多公司明确要求不能使用它。
template
class UniquePtr
{
public:
UniquePtr(T* ptr)
:_ptr(ptr)
{}
~UniquePtr()
{
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
//c++11
UniquePtr(const UniquePtr& ptr) = delete;
//c++98
/*private:
UniquePtr(const UniquePtr& ptr);*/
private:
T* _ptr;
};
直接不让资源进行拷贝,把拷贝构造给私有或者delete。
shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。
// 引用计数支持多个拷贝管理同一个资源,最后一个析构对象释放资源
//循环引用 -> 增加一个类weakptr,不增加引用计数即可
template
class SharedPtr
{
public:
SharedPtr(T* ptr = nullptr)
:_ptr(ptr)
,_pcount(new int(1))
,_mtx(new mutex)
{}
template
SharedPtr(T* ptr,D del)
:_ptr(ptr)
, _pcount(new int(1))
, _mtx(new mutex)
,_del(del)
{}
void release()
{
bool delete_flag = false;
_mtx->lock();
if (--(*_pcount) == 0)
{
if (_ptr)
{
_del(_ptr);
}
delete _pcount;
delete_flag = true;
}
_mtx->unlock();
if (delete_flag)
{
delete _mtx;
}
}
~SharedPtr()
{
release();
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* get()
{
return _ptr;
}
SharedPtr(const SharedPtr& sptr)
{
_ptr = sptr._ptr;
_pcount = sptr._pcount;
_mtx = sptr._mtx;
add_count();
}
int use_count()
{
return *_pcount;
}
void add_count()
{
_mtx->lock();
++_pcount;
_mtx->unlock();
}
SharedPtr& operator=(const SharedPtr& sptr)
{
if (_ptr != sptr._ptr)
{
if (--(*_pcount) == 0)
{
release();
}
_ptr = sptr._ptr;
_pcount = sptr._pcount;
_mtx = sptr._mtx;
add_count();
}
return *this;
}
private:
T* _ptr;
int* _pcount;
mutex* _mtx;
function _del = [](T* ptr) {delete ptr; };
};
因此在析构以及增加指针的引用计数时需要增加互斥锁来保证线程安全。
当我们在给一个双向循环链表结构用shared_ptr时,可能会出现循环引用问题。
// 解决方案:在引用计数的场景下,把节点中的_prev和_next改成weak_ptr就可以了
// 原理就是, node1->_next = node2; 和 node2->_prev = node1 时, weak_ptr 的 _next 和_prev 不会增加 node1 和 node2 的引用计数。
template
class WeakPtr
{
public:
WeakPtr()
:_ptr(nullptr)
{}
WeakPtr(const SharedPtr& sptr)
:_ptr(sptr.get())
{}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* get()
{
return _ptr;
}
private:
T* _ptr;
};
}
不是new出来的对象的话可以通过删除器来解决问题。
// 仿函数的删除器
template
struct FreeFunc {
void operator()(T* ptr)
{
cout << "free:" << ptr << endl;
free(ptr);
}
};
template
struct DeleteArrayFunc {
void operator()(T* ptr)
{
cout << "delete[]" << ptr << endl;
delete[] ptr;
}
};