允许从外部指针构造
这个版本的AutoPtr仍然还有很多不完美的地方,比如它在构造函数中,一定要new出一个对象,因此无法表达一个“空指针”,也无法“接管”一个已经存在的外部指针,另外,两个(同质的)AutoPtr<T>对象之间,如何赋值,靠被也未实现。
托管外来指针
让它可以托工外来的指针,暂时只要修改它的构造函数即可:
template <typename T>
struct AutoPtr
{
AutoPtr() //默认的构造过程
: _ptr(nullptr) //_ptr将是一个空指针
{}
explicit AutoPtr(T* ptr) //构造时,接管一个外部的指针
: _ptr(ptr)
{}
...//析构及其他原有操作不变
};
现在,我们可以这样使用AutoPtr<T>了:
AutoPtr <S> sap;
sap->a = 10; //不要这样,会死的,sap._ptr当前是NULL.
S* s = new S; //用普通的方式,创建了s指针,后面的代码中,千万不能delete s
AutoPtr <S> sap2(s); //把s交给sap2管
AutoPtr <S> sap3(new S); //也可以直接接管一个匿名的指针
AutoPtr <S> sap4 = new S; //编译不通过,关键字explicit起的作用
S s2; //这家伙来自栈,将自动释放
AutoPtr <S> sap5(&s2); //编译通过,但后果自负
001行构造sap时,调用的是默认构造函数(无入参),sap的_ptr成员被初始化为nullptr,
002行,调用sap->a,相当于执行“0->a”。执行结果不定
003行,在外部创建一个S的“堆对象”,
004行,通过sap2对它进行托管,因此在后面的代码中千万不鞥delete s。
005行,是一个好做法,只要有可能,就争取在创建自动指针的同时创建外部指针,并且令其成为一个匿名的对象(没有变量承载),避免引入重复产出一个对象的问题。
006行,sap4构造失败,原因是“explicit”关键字起了作用。尽管我们所模仿的auto_ptr允许此类操作,但这里不考虑将一个T*对象直接赋值给AutoPtr<T>对象功能(那会引入许多更复杂的要求)。
007行的s2是一个“栈对象”,所以它总是会被自动释放内存,将它托付给一个只晓得“我一定要负责干掉它”的所谓的“智能指针”,会酿成大错。
允许改变指向
当AutoPtr持有一个指针之后,如何释放呢?
先来为AutoPtr增加一个离婚的首先,函数名叫“release”。
template <typename T>
T* AutoPtr <T>::release() //放手
{
//放手, release,返回裸指针,智能指针不再占有内存
T* you_are_free_now = _ptr;
_ptr = nullptr;
return you_are_free_now;
}
注意,所谓“放手”,是将AutoPtr原来拥有的_ptr “归还”回去,这样可以方便“离婚”后的_ptr独立存在:
AutoPtr <int> ap(new int);//结合
int* tmp = ap.release();//分手,曾经的_ptr现在是陌生的“tmp”
接下来,是再婚手续,为了合法,会先强制离婚:
template <typename T>
T* AutoPtr <T>::reset(T* new_ptr)
{
//智能指针开始管理新的裸指针,返回旧的裸指针
T* old_ptr = release();
_ptr = new_ptr;//由来只闻新人笑
return old_ptr;//有谁听过旧人哭,旧人回娘家
}
设计一个完整的智能指针,所要考虑的事情远不止以上那些,比如:
如何判断智能指针是一个空指针?
如果比较两个智能指针?
如何在智能指针之间赋值?
【课堂作业】AutoPtr的实现
(1)根据以上代码,整理出一份相对完整,实际可用的AutoPtr,并写出配套的试用例子。
(2)考虑所写的AutoPtr是否支持处理数组指针,比如“AutoPtr <int> ap(new int [5])”,如果要支持,应如何改进?