我们知道堆上申请的空间必须通过手动释放来回收,如果忘记这一点,很容易造成内存泄漏。智能指针的引入就是为了解决如何在正确的时机(已经没有利用价值)释放堆上申请的空间,其实现原理是通过在指针外面包一层类,并在栈上生成此类的对象,当它在栈上被自动回收的时候,将堆上的空间也释放掉。
1. 重新造一个轮子
自定义智能指针遵循以下两个原则:
1). 智能指针类必须定义为类模板,这样才能hold住所有具体类
2). 引用计数必须包含在具体类中,即只在最源头保存一个引用计数
ReferenceCounted.h
所有具体类必须继承自ReferenceCounted类。其内部包含引用计数,当被一个SmartPtr引用时就自增,当一个SmartPtr被回收时就自减。当引用计数减为0时,通过调用delete this显式地将自己回收。
#ifndef REFERENCECOUNTED_H
#define REFERENCECOUNTED_H
class ReferenceCounted
{
public:
ReferenceCounted()
{
referenceCounter = 0;
}
void AddRef()
{
++referenceCounter;
}
void Release()
{
if (referenceCounter > 0 && --referenceCounter == 0)
{
delete this;
}
}
protected:
// 虚析构函数!
virtual ~ReferenceCounted(){}
private:
// 引用计数
int referenceCounter;
};
#endif
SmartPtr.h
SmartPtr类被定义为类模板,可以接收任何继承自ReferenceCounted类的具体类。
#ifndef SMARTPTR_H
#define SMARTPTR_H
// ReferenceCountedT必须是一个继承自ReferenceCounted的类
template <class ReferenceCountedT>
class SmartPtr
{
public:
// 构造函数一:传入类型为ReferenceCountedT的指针来构造SmartPtr,必须保证p所指向的内容在堆上
SmartPtr(ReferenceCountedT *p);
// 构造函数二:拷贝构造函数,用一个SmartPtr来构造另一个SmartPtr
SmartPtr(const SmartPtr<ReferenceCountedT> &p);
~SmartPtr();
// 重载赋值运算符一:必须保证p所指向的内容在堆上
SmartPtr& operator =(const ReferenceCountedT *p);
// 重载赋值运算符二:智能指针间赋值
SmartPtr& operator =(const SmartPtr<ReferenceCountedT> &p);
// 重载取值运算符
ReferenceCountedT& operator *() const;
// 重载成员选择运算符
ReferenceCountedT* operator ->() const;
// 重置SmartPtr的内部指针
void Reset(ReferenceCountedT *p = NULL);
// 获得内部指针
ReferenceCountedT* Get();
private:
void Dispose();
ReferenceCountedT *ptr;
};
// 用法
// ReferenceCounted *p = new ReferenceCounted();
// SmartPtr<ReferenceCounted> sp(p);
template <class ReferenceCountedT>
SmartPtr<ReferenceCountedT>::SmartPtr(ReferenceCountedT *p)
{
ptr = p;
if (ptr)
{
ptr->AddRef();
}
}
// 用法
// ReferenceCounted *p = new ReferenceCounted();
// SmartPtr<ReferenceCounted> sp1(p);
// SmartPtr<ReferenceCounted> sp2(sp1);
template <class ReferenceCountedT>
SmartPtr<ReferenceCountedT>::SmartPtr(const SmartPtr<ReferenceCountedT> &p)
{
ptr = p.ptr;
if (ptr)
{
ptr->AddRef();
}
}
template <class ReferenceCountedT>
SmartPtr<ReferenceCountedT>::~SmartPtr()
{
Dispose();
}
template <class ReferenceCountedT>
SmartPtr<ReferenceCountedT>& SmartPtr<ReferenceCountedT>::operator =(const ReferenceCountedT *p)
{
Reset(p);
return *this;
}
template <class ReferenceCountedT>
SmartPtr<ReferenceCountedT>& SmartPtr<ReferenceCountedT>::operator =(const SmartPtr<ReferenceCountedT> &p)
{
Reset(p.ptr);
return *this;
}
template <class ReferenceCountedT>
ReferenceCountedT& SmartPtr<ReferenceCountedT>::operator *() const
{
return *ptr;
}
template <class ReferenceCountedT>
ReferenceCountedT* SmartPtr<ReferenceCountedT>::operator ->() const
{
return ptr;
}
template <class ReferenceCountedT>
void SmartPtr<ReferenceCountedT>::Reset(ReferenceCountedT *p)
{
// 避免自赋值self-assignment
if (ptr != p)
{
Dispose();
ptr = p;
if (ptr)
{
ptr->AddRef();
}
}
}
template <class ReferenceCountedT>
ReferenceCountedT* SmartPtr<ReferenceCountedT>::Get()
{
return ptr;
}
template <class ReferenceCountedT>
void SmartPtr<ReferenceCountedT>::Dispose()
{
if (ptr)
{
ptr->Release();
}
}
#endif
测试代码:
MyClass.h
MyClass类表示一个具体类,继承自ReferenceCounted。
#include "ReferenceCounted.h"
class MyClass : public ReferenceCounted
{
public:
MyClass()
{
num = 0;
}
int MyFoo()
{
++num;
return num;
}
int num;
protected:
~MyClass(){}
};
Main.cpp
#include "SmartPtr.h"
#include "MyClass.h"
#include <crtdbg.h>
#include <vector>
#include <iostream>
using namespace std;
void main(void)
{
{
MyClass *rc1 = new MyClass();
SmartPtr<MyClass> sp1 = rc1; // 普通构造函数
SmartPtr<MyClass> sp2 = sp1; // 拷贝构造函数
SmartPtr<MyClass> sp3(rc1); // 普通构造函数
SmartPtr<MyClass> sp4(sp1); // 拷贝构造函数
MyClass *rc2 = new MyClass();
sp3 = rc2; // 赋值运算符
sp4 = sp3; // 赋值运算符
cout << (*sp1).MyFoo() << endl; // 像指针一样操作
cout << sp1->MyFoo() << endl;
vector<SmartPtr<MyClass>> v; // 在vector中的表现
v.push_back(sp1);
v.push_back(sp2);
v.push_back(sp3);
v.push_back(sp4);
}
_CrtDumpMemoryLeaks();
}