条款28:smart pointer(智能指针)
1.Smart Pointers 的构造,赋值,析构
smart pointer的构造行为很明确:确定一个目标物,然后让smart pointer 内部dumb pointer 指向它.如果尚未决定目标物,就将内部指针设为0
smart pointer的拷贝构造与拷贝赋值和析构的实现稍微复杂,以auto_ptr pointer 为例,如果只是用默认版本的拷贝构造和赋值,会发生两个指针指向同一块内存的情况,这是我们不希望的.
auto_ptr采用了更弹性的做法:当auto_ptr被复制或者被赋值,其"对象拥有权"会被转移.这将会导致当以by value 的方式传递auto_ptr是个非常糟糕的主意,形参将会获得对象的拥有权,函数作用域结束后,形参将会调用析构,最终指向的对象消失不见.正确的做法是以pass-by-reference-to-const传递auto_ptr.
有趣的是通常拷贝构造和拷贝赋值都是const成员函数,在auto_ptr则不是,因为需要改变"对象拥有权".
2.实现Dereferencing Operators(解引操作符)
实现解引(*)操作的函数必须返回引用类型,否则该智能指针无法体现多态性,返回引用正确且效率高.
实现operator -> 也类似,返回类型大部分时候应该是dumb指针,所以可以体现多态性.
3.测试smart pointers是否为null
其实为我们的smart pointer类加上isNull函数很简单明了,但不够像原生指针那么自然,
SmartPtr<TreeNode> ptn;
...
if (ptn == 0) // 错误
if (ptn) // 错误
if (!ptn) // 错误
有一种做法是提供一个隐式类型转换操作符 operator void*();判断dumbptr是否为null,这样一来,ptn可以隐式的转换成null,但这样有个缺点,在你认为它会编译不通过的时候,它却神奇的执行了,更明确的说,它允许指针指针与完全不同的类型进行比较
SmartPtr<Orange> s1;
SmartPtr<Apple> s2;
if ( s1 == s2 ) // 类型不同却编译通过
这样的隐式转换函数非常危险,某些程序员喜欢转成bool或者const void*,但没有一个可以消除"不同类型可以进行比较"的问题
More Effective C++一书指出,能够将上述麻烦出现的机会降到最低的方法,就是重载"!"操作符.
也就是bool operator ! () const,当smartptr是null时返回true.
比较null的时候再也不能用if (ptn == 0) 或者if (ptn) 等,只能用if (!ptn)来判断ptn.
唯一需要考虑的风险就是
SmartPtr<apple> s1;
SmartPtr<Orange> s2;
if (!s1 == !s2) // 可以通过编译
但我想没有多少人会写出(!s1 == !s2)这段代码
4.将Smart Pointers转换为Dumb Pointers
我们可以在samrt pointer-to-T 加上一个隐式类型转换操作符,使之可转换为dumb pointer-to-T,但这样做的坏处是让客户得以轻易地直接对dumb pointers做动作,回避了智能指针当初设计的目的.而且,如果智能指针采用引用计数,那么允许操控dumb pointer一定会使簿记方面的错误.
如果你提供了一个隐式转换操作符,可以将smart pointer 转换为内部的 dumb pointer,由于编译器禁止一次施行一个以上这类转换,我们无法将"DBPtr<Tuple>转换为Tuple*,并且将Tuple*转换为TupleAccessors".(第一个转换是通过smartpointer内的Tuple*()转换函数,第二个转换是通过TupleAccessors(const Tuple* pt)单一自变量转换函数).
不仅如此,考虑以下代码:
DBPtr<Tuple> pt = new Tuple;
...
delete pt;
咋一看,对象怎么能delete呢,但是,编译器会竭尽所能让语法通过编译,它会让pt隐式的转换为Tuple*,然后删除,这一定会带来很大的麻烦
所以,不要提供对dumb pointers的隐式转换操作符,除非不得已.
5.smart pointers和"与继承有关的"类型转换
直接上结论:只要你能够将T1* 隐式转换成T2*,你便能够用以下代码将smart pointer-to-T1隐式转换为smart pointer-to-T2
template<class newType>
operator SmartPtr<newType>() {
return smartPtr<newType>(pointee);
}
这就是smart pointer的隐式类型转换,非常强大