<<More Effective C++>> 读书笔记

本文详细探讨了智能指针(如auto_ptr)的构造、赋值、析构行为,强调了对象所有权转移的重要性,并提醒了不当使用可能导致的问题。解引用操作符的实现应返回引用以保持多态性。文章指出,测试智能指针是否为null时,应避免隐式转换为void*,而应重载!操作符。同时,不应提供到原始指针的隐式转换,以防直接操作智能指针底层指针。此外,讨论了智能指针的类型转换,允许从smartpointer-to-T1隐式转换为smartpointer-to-T2。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

条款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的隐式类型转换,非常强大

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值