说起智能指针,不少人都不陌生,比如auto_ptr、shared_ptr、unique_ptr、weak_ptr。根据shared_ptr的功能,自己仿造也实现了个。
对于shared_ptr这种智能指针,有一个共享的引用计数器来控制指针对象的销毁,当引用计数器变为0时,则销毁指针指向的对象。对于多线程安全问题,我在代码中使用的Interlocked系列的原子操作函数。
在学习过程中,渐渐学会了RAII(Resource Acquisition Is Initialization),慢慢领略到了这种模式的好处。
直接上代码:
SmartPtr.hpp
#pragma once
#include "stdafx.h"
#include <assert.h>
#include <windows.h>
//#define DEBUG_SMARTPTR
template<typename T>
class SmartPtr;
template <typename T>
class RefPtr
{
friend class SmartPtr<T>;
explicit RefPtr(T *p) :pointer(p), nUse(0)
{
assert(pointer);
#ifdef DEBUG_SMARTPTR
std::cout << "Create Pointer!" << std::endl;
#endif
}
RefPtr(const RefPtr&)
{
}
RefPtr& operator= (const RefPtr & ref)
{
}
~RefPtr()
{
#ifdef DEBUG_SMARTPTR
std::cout << "Delete Pointer!" << std::endl;
#endif
assert(pointer);
if (pointer != NULL)
{
delete pointer;
pointer = NULL;
}
}
unsigned int AddRefCount()
{
return InterlockedIncrement((unsigned int*)&nUse);
}
unsigned int SubRefCount()
{
return InterlockedDecrement((unsigned int*)&nUse);
}
bool AddRefCount_lock()
{
for (;;)
{
unsigned int temp = nUse;
if (temp == 0)
{
return false;
}
if (InterlockedCompareExchange((unsigned int *)&nUse, temp + 1, temp) == temp)
{
return true;
}
}
}
volatile unsigned int nUse;
T *pointer;
};
template<typename T>
class SmartPtr
{
public:
explicit SmartPtr(T *pointer) :ptr(new RefPtr<T>(pointer))
{
assert(pointer);
#ifdef DEBUG_SMARTPTR
std::cout << "Create SmartPointer!" << std::endl;
#endif
ptr->AddRefCount();
}
explicit SmartPtr(const SmartPtr<T>& sp) :ptr(sp.ptr)
{
#ifdef DEBUG_SMARTPTR
std::cout << "Copy0 SmartPointer!" << std::endl;
#endif
ptr->AddRefCount();
}
explicit SmartPtr(const SmartPtr<T>* sp) :ptr(sp->ptr)
{
#ifdef DEBUG_SMARTPTR
std::cout << "Copy1 SmartPointer!" << std::endl;
#endif
ptr->AddRefCount();
}
SmartPtr& operator=(const SmartPtr<T>& sp)
{
if (sp.ptr != ptr)
{
//注意先加后减,防止指向同对象析构的问题
if (sp.ptr->AddRefCount_lock())
{
if (ptr->SubRefCount() == 0)
{
delete ptr;
}
ptr = sp.ptr;
}
}
#ifdef DEBUG_SMARTPTR
std::cout << "Copy2 SmartPointer!" << std::endl;
#endif
return *this;
}
T* operator->()
{
return GetPtr();
}
T* operator->() const
{
return GetPtr();
}
T& operator*()
{
return *ptr->pointer;
}
T& operator*() const
{
return *ptr->pointer;
}
bool operator!()
{
return !ptr;
}
~SmartPtr()
{
if (ptr->SubRefCount() == 0)
{
delete ptr;
}
#ifdef DEBUG_SMARTPTR
std::cout << "Delete SmartPointer!" << std::endl;
#endif
}
int GetRefCount() const
{
return ptr->nUse;
}
bool isNull()
{
return ptr->pointer == NULL;
}
T* GetPtr() const
{
assert(ptr->pointer);
return ptr->pointer;
}
//返回对象
T GetValue() const
{
assert(ptr->pointer);
return *ptr->pointer;
}
private:
RefPtr<T> *ptr;
};
//兼容const比较
template<typename T>
inline bool operator==(const SmartPtr<T>& a,const SmartPtr<T>& b)
{
return a.GetPtr() == b.GetPtr();
}
template<typename T>
inline bool operator!=(const SmartPtr<T>& a,const SmartPtr<T>& b)
{
return a.GetPtr() != b.GetPtr();
}
test.cpp
#include "SmartPtr.hpp"
#include <iostream>
#include <process.h>
#define THREADCOUNT 10
typedef struct _PERSON_
{
char szName[20];
int nAge;
~_PERSON_()
{
std::cout << "Person Distructor!"<< std::endl;
}
}PERSON;
SmartPtr<PERSON> g_p(new PERSON{ "g_test", 12 });
unsigned int __stdcall testThread(void *pParam)
{
SmartPtr<PERSON> sp((SmartPtr<PERSON> *)pParam);
g_p = sp;
std::cout << sp->nAge << std::endl;
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
do
{
HANDLE hThread[THREADCOUNT];
SmartPtr<PERSON> p0(new PERSON{ "Jovi", 12 });
SmartPtr<PERSON> p1(new PERSON{ "Solanin", 13 });
SmartPtr<PERSON> p2(p1);
const SmartPtr<PERSON> p3(p1);
SmartPtr<PERSON> p4(p3);
std::cout << (p3 == p1) << std::endl;
std::cout << (p2 == p0) << std::endl;
std::cout << (p3 != p1) << std::endl;
std::cout << (p2 != p0) << std::endl;
p4 = p0;
SmartPtr<PERSON> *p = new SmartPtr<PERSON>(p1);
for (int i = 0; i < THREADCOUNT; i++)
{
hThread[i] = (HANDLE)_beginthreadex(NULL, 0, testThread, (void *)p, 0, 0);
// WaitForSingleObject(hThread[i], INFINITE);
}
WaitForMultipleObjects(THREADCOUNT, hThread, TRUE, INFINITE);
delete p;
} while (0);
system("pause");
return 0;
}
此处的do while(0)只是我想在pause前打印出所有析构函数的输出。
对于基于引用计数器的智能指针,最致命缺点就是循环引用,导致对象被长期占用,无法释放。
以上是我对智能指针的实现和个人看法,如有不正确的地方,欢迎指出。