在上一篇博客里的智能指针的剖析中,我们是采用移交管理权的方式,并且把给出管理权的一方的指针空。
在对智能指针的再次剖析中我们仍然采用移交管理权的方式,但是在这种方式下我们并没有把管理权的一方的指针空,仍然可以对这块空间进行解引用、访问等操作,但是我们是没有权利去析构的。这样就会避免因为多次析构同一块内存空间而造成程序崩溃。
因此我们采用另一种方法:
在类的成员变量里多添加一个bool类型的成员变量
template<class T>
class AutoPtr
{
public:
AutoPtr(T* pStr = NULL, bool isPod = true)
:_pStr(pStr)
, IsPod(isPod)
{
cout << "AutoPtr()" << endl;
}
AutoPtr(AutoPtr& ad)
:_pStr(ad._pStr)
, IsPod(ad.IsPod)
{
ad.IsPod = false;
cout << "&AutoPtr()" << endl;
}
AutoPtr<T>& operator=(AutoPtr& ad)
{
if(this!=&ad)//判断是不是自己给自己赋值
{
delete _pStr;
_pStr = ad._pStr;
if (_pStr)
{
IsPod = true;
}
}
ad.IsPod = false;
return *this;
}
~AutoPtr()
{
if (IsPod)
{
delete _pStr;
cout << "~AutoPtr()" << endl;
}
}
T& operator*()
{
return *_pStr;
}
T* operator->()
{
return _pStr;
}
private:
T* _pStr;
bool IsPod;
};
void FunTest()
{
AutoPtr<int> ap(new int);
AutoPtr<int> ap1(ap);
AutoPtr<int> ap2;
ap2 = ap1;
*ap = 10;
*ap1 = 20;
*ap2 = 30;
}
这种方式虽然也是移交了管理权但是并没有把给出管理权的一方的指针置空,因此它仍然能够指向这块内存空间,只是失去了对这块内存空间释放的权利。显然这种方法能够解决可以都对ap,ap1,ap1进行解引用的操作,但是同样它也引入了新的问题如果我们将ap2=ap1的操作更改成ap2=ap,那么程序照样会崩溃,因为ap1里面的bool变量仍然是true,因此会将这块内存空间释放两次,造成程序崩溃。
在这里还有一个关于无名对象的问题:
void FunTest()
{
AutoPtr<int> ap1(AutoPtr<int>(new int));
AutoPtr<int> ap2;
ap2 = ap1;
}
如果我们使用无名对象去创建ap1,我们可能认为会去调用它的拷贝构造函数,一旦调用了拷贝构造函数可呢就会修改这个无名对象里的内容,但无名对象是具有常性的,如果修改了就会违背const的约束,按理说会出现编译错误,但实际上并没有?为什么?
但实际上们在逐步调试这个程序的时候会发现并没有去调用它的拷贝构造函数,只是去调用了它的构造函数,因为无名对象在使用过后在整个程序中就失去了作用,对于这种情况,c++会优化掉该临时对象的产生,直接以相同参数调用相关构造函数,也就是说AutoPtr ap1(AutoPtr(new int))直接会被被系统优化成AutoPtr ap1(new int)。
小结:上一篇博客中的智能指针去管理内存空间的方式同样存在缺陷,而这篇也是存在缺陷的,两种方式各有优缺点,但是两者我更青睐于第一种,我觉的多次释放同一块内存的问题比访问冲突要严重,当然啦~博客中如果有什么错误,希望大家能提出来~