关于在抛异常中我们可能会忘记将内存释放掉造成内存泄漏,为了避免这种情况的发生我们引入了智能指针,我们把自己申请的内存交给这个AutoPtr类对象去管理,系统会自动调用析构函数就能将资源清理防止内存泄漏。
template<class T>
class AutoPtr
{
public:
AutoPtr(T* p)
: _p(p)
{
cout << "AutoPtr()" << endl;
}
~AutoPtr()
{
cout << "~AutoPtr()" << endl;
if (_p)
{
delete _p;
_p = NULL;
}
}
protected:
T* _p;
};
void FunTest()
{
int* p = new int;
AutoPtr<int> ap(p);
try
{
FILE* fp = fopen("1.txt", "rb");
if (fp == NULL)
{
throw 1;
}
}
catch (char err)
{
cout << "FunTest()" << endl;
}
}
int main()
{
try
{
FunTest();
}
catch (int err)
{
cout << "FunTest" << endl;
}
system("pause");
return 0;
}
上述是为了解决忘记释放内存而造成内存泄漏的问题,但是关于上面的智能指针类存在着许多的问题 。
接下来看看存在哪些问题
void FunTest()
{
int *p = new int;
AutoPtr<int> ap(p);
//AutoPtr<int> ap1(ap);
AutoPtr<int> ap2;
ap2 = ap;
}
无论我们用ap这个对象去构造ap1这个对象还是使用ap这个对象去给ap2赋值程序都会崩溃。
因为你ap这个对象管理了指针变量p所指向的内存空间,然后你拷贝构造是默认的是简单的值拷贝(浅拷贝),因此ap1这个对象也管理了指针变量p所指向的内存空间,于是你在析构的时候会因为多次析构同一块内存空间造成程序崩溃,赋值和拷贝构造同理。
为了解决 浅拷贝带来多次析构同一块内存空间,我们自己定义拷贝构造函数和将赋值运算符重载
template<class T>
class AutoPtr
{
public:
AutoPtr(T* pStr = NULL)
: _pStr(pStr)
{
cout << "AutoPtr()" << endl;
}
AutoPtr(AutoPtr &ad)//通常拷贝构造都是const类型的但这里是要修改这个传入的参数的因此不能是const类型
:_pStr(ad._pStr)//移交管理权
{
if (_pStr)
{
ad._pStr = NULL;
}
}
AutoPtr<T>& operator =(AutoPtr &ad)
{
if(this!=&ad)//判断是不是自己给自己赋值
{ delete _pStr;
_pStr = ad._pStr;
ad._pStr = NULL;
return (*this);
}
}
~AutoPtr()
{
if (_pStr)
{
cout << "~AutoPtr()" << endl;
delete _pStr;
_pStr = NULL;
}
}
T& operator*()
{
return *_pStr;
}
T* operator->()
{
return _pStr;
}
protected:
T* _pStr;
};
但是此时这个智能指针仍然存在问题
void FunTest()
{
int *p = new int;
AutoPtr<int> ap(p);
AutoPtr<int> ap1(ap);
*ap1 = 10;
*ap=20;
}
此时我们对这个ap解引用就会出错,因为它所指向的内存空间已经被置空了,此时是不能去往里面写入内容的。
小结一下:这种版本的智能指针是极易出错的,因此不建议使用这种这种智能指针,陷进太多,容易造成内存泄漏,或者非法的解引用。