智能指针存在的必要性
- malloc出来的空间,没有进行释放,存在内存泄漏的问题。
- 异常安全问题。如果在malloc和free之间如果存在抛异常,那么还是有内存 泄漏。这种问题就叫异常安全
智能指针的使用及原理
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。 在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处: 不需要显式地释放资源。 采用这种方式,对象所需的资源在其生命期内始终保持有效。 原理: 1. RAII特性 2. 重载operator*和opertaor->,具有像指针一样的行为 3. 解决浅拷贝的问题
C++98中的智能指针
auto_ptr: edition1: 如何解决浅拷贝问题:资源转移。 缺点:转移资源,若此时想对原对象操作会出错。
namespace bit
{
template<class T>
class auto_ptr
{
public:
auto_ptr(T* ptr=nullptr)
:_ptr(ptr)
{}
auto_ptr(auto_ptr<T>& pt):_ptr(pt._ptr)
{
pt._ptr = nullptr;
}
auto_ptr<T>& operator=(auto_ptr<T>& ap)
{
if (this != &ap)
{
if (_ptr)
{
delete _ptr;
}
_ptr = ap._ptr;
ap._ptr = nullptr;
}
return *this;
}
~auto_ptr()
{
if (_ptr)
{
delete _ptr;
_ptr = nullptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* Get()
{
return _ptr;
}
private:
T* _ptr;
};
};
editio2:转移释放权限
namespace bit
{
template<class T>
class auto_ptr
{
public:
auto_ptr(T* ptr =nullptr)
:_ptr(ptr)
,owner(false)
{
if (_ptr)
{
owner = true;
}
}
auto_ptr(auto_ptr<T>& t)
:_ptr(t._ptr)
,owner(t.owner)
{
t.owner = false;
}
auto_ptr<T>& operator=(auto_ptr<T>& t)
{
if (this != &t)
{
if (this->_ptr && this->owner)
{
delete _ptr;
}
_ptr = t._ptr;
owner = t.owner;
t.owner = false;
}
return *this;
}
~auto_ptr()
{
if (_ptr && owner)
{
delete _ptr;
owner = false;
_ptr = nullptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* Get()
{
return _ptr;
}
private:
T* _ptr;
bool owner;
};
}
void TestAutoPtr()
{
bit::auto_ptr<int> ap1(new int);
*ap1 = 100;
bit::auto_ptr<int> ap2(ap1);
int* p1 = new int;
int* p2(p1);
*p1 = 10;
*p2 = 20;
delete p1;
p1 = p2 = nullptr;
if (ap2.Get())
*ap2 = 2000;
if (ap1.Get())
*ap1 = 1000;
bit::auto_ptr<int> ap3(new int);
*ap3 = 300;
bit::auto_ptr<int> ap4(new int);
*ap4 = 400;
ap3 = ap4;
if (true)
{
bit::auto_ptr<int> ap5(ap2);
*ap5 = 100;
*ap2 = 200;
*ap1 = 300;
}
*ap2 = 10;
}
int main()
{
TestAutoPtr();
return 0;
}
缺陷:可能会导致野指针的情况,比如上面的示例 所以:建议不要使用auto_ptr
C++11中的智能指针
C++11中unique_ptr指针
这个智能指针的作用就是防拷贝,既然拷贝可能会带来资源重复释放,资源泄露的问题,那我从根源上就不让你拷贝。
template<class T>
class DFDef
{
public:
void operator()(T*& ptr)
{
if (ptr)
{
delete ptr;
ptr = nullptr;
}
}
};
template<class T>
class Free
{
public:
void operator()(T*& ptr)
{
if (ptr)
{
free(ptr);
ptr = nullptr;
}
}
};
class FClose
{
public:
void operator()(FILE*& ptr)
{
if (ptr)
{
fclose(ptr);
ptr = nullptr;
}
}
};
namespace bite
{
template<class T, class DF = DFDef<T>>
class unique_ptr
{
public:
unique_ptr(T* ptr = nullptr)
: _ptr(ptr)
{}
~unique_ptr()
{
if (_ptr)
{
DF df;
df(_ptr);
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* Get()
{
return _ptr;
}
private:
unique_ptr(const unique_ptr<T, DF>&);
unique_ptr<T, DF>& operator=(const unique_ptr<T, DF>&);
private:
T* _ptr;
};
}
#include <memory>
void TestUniquePtr()
{
bite::unique_ptr<int> up1(new int);
bite::unique_ptr<int, Free<int>> up2((int*)malloc(sizeof(int)));
bite::unique_ptr<FILE, FClose> up3(fopen("12345.txt", "w"));
}
int main()
{
TestUniquePtr();
bite::unique_ptr<int> up1(new int);
bite::unique_ptr<int> up3(new int);
unique_ptr<int> up4(new int);
unique_ptr<int> up6(new int);
return 0;
}
C’++11中的shared_ptr
template<class T>
class DFDef
{
public:
void operator()(T*& ptr)
{
if (ptr)
{
delete ptr;
ptr = nullptr;
}
}
};
template<class T>
class Free
{
public:
void operator()(T*& ptr)
{
if (ptr)
{
free(ptr);
ptr = nullptr;
}
}
};
class FClose
{
public:
void operator()(FILE*& ptr)
{
if (ptr)
{
fclose(ptr);
ptr = nullptr;
}
}
};
#include <mutex>
namespace bite
{
template<class T, class DF = DFDef<T>>
class shared_ptr
{
public:
shared_ptr(T* ptr = nullptr)
: _ptr(ptr)
, _pCount(nullptr)
, _pMutex(new mutex)
{
if (_ptr)
{
_pCount = new int(1);
}
}
~shared_ptr()
{
Release();
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* Get()
{
return _ptr;
}
int use_count()const
{
return *_pCount;
}
shared_ptr(const shared_ptr<T>& sp)
: _ptr(sp._ptr)
, _pCount(sp._pCount)
, _pMutex(sp._pMutex)
{
AddRef();
}
shared_ptr<T, DF>& operator=(const shared_ptr<T, DF>& sp)
{
if (this != &sp)
{
Release();
_ptr = sp._ptr;
_pCount = sp._pCount;
_pMutex = sp._pMutex;
AddRef();
}
return *this;
}
private:
void AddRef()
{
if (nullptr == _ptr)
return;
_pMutex->lock();
++(*_pCount);
_pMutex->unlock();
}
void Release()
{
if (nullptr == _ptr)
return;
bool isDelete = false;
_pMutex->lock();
if (_ptr && 0 == --(*_pCount))
{
DF df;
df(_ptr);
delete _pCount;
_pCount = nullptr;
isDelete = true;
}
_pMutex->unlock();
if (isDelete)
{
delete _pMutex;
}
}
private:
T* _ptr;
int* _pCount;
mutex* _pMutex;
};
}
void TestSharedPtr()
{
bite::shared_ptr<int> sp1(new int);
bite::shared_ptr<int> sp2(sp1);
bite::shared_ptr<int> sp3(new int);
bite::shared_ptr<int> sp4(sp3);
sp3 = sp2;
sp4 = sp2;
}
struct A
{
int a = 0;
int b = 0;
int c = 0;
};
void ThreadFunc(bite::shared_ptr<A>& sp, int n)
{
for (int i = 0; i < n; ++i)
{
bite::shared_ptr<A> copy(sp);
copy->a++;
copy->b++;
copy->c++;
}
}
#include <thread>
void TestSharedPtrSafe()
{
bite::shared_ptr<A> sp(new A);
thread t1(ThreadFunc, sp, 10000);
thread t2(ThreadFunc, sp, 10000);
t1.join();
t2.join();
cout << sp->a << endl;
cout << sp->b << endl;
cout << sp->c << endl;
}
#include <memory>
void ThreadFuncstd(shared_ptr<A>& sp, int n)
{
for (int i = 0; i < n; ++i)
{
shared_ptr<A> copy(sp);
copy->a++;
copy->b++;
copy->c++;
}
}
void TestSharedPtrSafestd()
{
shared_ptr<A> sp(new A);
thread t1(ThreadFuncstd, sp, 10000);
thread t2(ThreadFuncstd, sp, 10000);
t1.join();
t2.join();
cout << sp->a << endl;
cout << sp->b << endl;
cout << sp->c << endl;
}
int main()
{
TestSharedPtrSafestd();
shared_ptr<int> sp(new int);
TestSharedPtrSafe();
TestSharedPtr();
return 0;
}
shared_ptr:只保证的是自身引用计数的安全,不保证用户在多线程下的线程安全。所以为了保证安全的话,用户得使用atomic(C++11中的)或者加锁的方式。
shared_ptr循环引用所引发的问题。
struct ListNode
{
shared_ptr<ListNode> next;
shared_ptr<ListNode> prev;
int data;
ListNode(int x)
: data(x)
{
cout << "ListNode(int):" << this << endl;
}
~ListNode()
{
cout << "~ListNode():" << this << endl;
}
};
void TestLoopRef()
{
shared_ptr<ListNode> sp1(new ListNode(10));
shared_ptr<ListNode> sp2(new ListNode(20));
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
sp1->next = sp2;
sp2->prev = sp1;
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
}
int main()
{
TestLoopRef();
}
资源泄露的问题。
如何解决shared_ptr所造成的循环调用所引起的内存泄露的问题
|