原则28:智能指针

智能指针不是指针而是一种对象。
内建指针被称为dumb pointer。
智能指针可以避免内存泄漏的问题,它们在赋值和复制方面灵活可控,它们允许你灵活控制智能指针的解引用操作。
书中展示了智能指针类模版的内部一般构造。智能指针都有一个内部成员指针指向真正的对象。
对于auto_ptr而言以值传递方式传值是非常危险的。一般你是把一个auto_ptr对象传递到某函数体内,那么此时auto_ptr所指对象也被搬移到函数体内,可是函数是局部作用域它结束完以后,处在它体内的auto_ptr对象就会被销毁,那么函数体外的auto_ptr早就已经无物可指,那么在它的作用域结束以后,它的析构函数自动被调用的话,就会删除一块本不该删除的内存造成未定义行为。这一点是由auto_ptr转移对象所有权的特性决定的。所以你不能通过值传递传递auto_ptr,你可以考虑使用const引用传递。
智能指针auto_ptr的复制构造函数和复制操作符的实现函数声明中都是const参数,但是在它们的实现代码中缺有意去掉了这些const并进行必要的修改,主要是实现所有权转移。
智能指针类的成员函数都是函数模版。
智能指针的析构函数通常是判断所指之物是否是当前智能指针所指,如果是则删除。虽然这是通用的模式,但是各种类智能指针的处理方式也不尽相同,有的是相当简单直接,有的异常复杂。
只能指针的解引用操作符一般返回的是对象的应用而不是对象。这是因为如果返回的是对象,它可能返回派生类的对象,而这时调用的virtual函数也许并不是你想要的,这可能引起灾难性的后果。
智能指针中的->操作符,但是operator->必须返回一个指针,而这个指针必须是指向智能指针中所指之物的内建指针。
原则28_1:测试智能指针是否为NULL
作者提出了在智能指针类内添加IsNULL方法的办法,但是我不知道所谓的“在测试NULL时智能指针与内建指针行为不相似的问题”到底是啥意思。
另一种方法是C++中的类型转换函数。即,把智能指针都转换成void类型,并在指针为NULL时返回0否则返回非0。这样一来不同类型的智能指针就能够互相比较了,因为它们都是void。但是,不同类型的只能指针之间的比较一般来讲没有意义,所以应该避免不同类型只能指针之间的比较。
作者推荐的方法是重载operator!操作符,仅当智能指针为NULL时返回TRUE否则返回FALSE。那么这时你可以比较两种不同类型智能指针是否都为NULL。


原则28_2:把智能指针转换成内建指针
出于调用的方便,你希望在函数参数是非智能指针类型,而输入参数却是智能指针类型的时候,让智能指针自动地返回其内含的内建指针,这时函数传参就会成功,同时也解决了判断智能指针为NULL的问题。
实现这种用途的办法很简单,你只需重载解引用操作符,使其返回内建指针即可。
不过这会带来极大的隐患。
1、智能指针的初衷是封装对内建指针的内存操作,而隐式转换破坏了这种封装。拿带有引用计数功能的智能指针来说,这会破坏正常的引用计数功能;
2、C++同时只允许至多1次的用户自定义类型转换,从智能指针类型到内建类型属于用户自定义类型转换。假设现在包含内建类型B的智能指针A要送到形参类型为C的函数中去,那么就要经历A隐式转换成B,B再转换成C的过程,这在C++中是绝对不被允许的。
3、会导致BUG的产生。
假设现在有代码:A pt= new B;……;delete pt;因为智能指针是用一个对象来封装了指针,但实际上它仍然是个对象,你用delete删除一个对象,这不合理啊。C++为了通过编译会把pt转换成B 类型,这样就可以用delete删除之。所以这就会造成对象被两次删除,一次是智能指针本身的删除,另一次是你手动delete。关于这一点我觉得书上说的有些不明白或者可以说是错误。
所以不要轻易破坏智能指针的封装。
原则28_3:智能指针和继承类到基类的类型转换
在默认情况下C++不会把智能指针里面的类型向父类转换,这是因为智能指针封装了类类型,呈现给外界的是智能指针对象,这与父类类型一点关系没有。
书中提到了一种破坏封装的解决办法,即,在智能指针内部提供向父类类型转换的操作符,返回父类内建指针。
此外,人为地添加隐式类型转换操作符会破坏智能指针的通用性。还有,如果继承层次很深你就必须添加很多隐式类型转换操作符到该类型的智能指针类中,这费劲还容易出错。如果你不这样做,那么智能指针类中只包含到直接父类的转换操作符,然后直接父类再利用它的隐式类型转换操作符转换成间接父类,这就不止一次转换了。如果这发生在同一时间是不被C++允许的。
由于为特定类型的智能指针类添加隐式类型转换操作符属于特例不通用,所以在这里可以为智能指针模版添加成员函数模版以达到添加通用的隐式类型转换函数的目的。本原则中还解释了它的原理,其实这很显而易见。模板的定义是如下所示:
原则28:智能指针_第1张图片

它是把当前智能指针的内建指针按照C语言的风格进行了一下强制类型转换,而且可以看到函数模版的类型参数与智能指针的类型参数不同,因此它们的实际类型可以毫不相干可以进行任意类型间的转换,何况是父子类型。内建指针就是普通的指针,用它初始化智能指针肯定可行。子类指针转换成父类指针也是可行的,那么这种转换方法肯定是可行的。而且这还可以解决上述子类转换成间接父类造成的二次转换问题。
当然它也有它的缺点,1、可移植性差,因为支持成员函数模版的编译器少。2、该方法的原理不甚明了,因此就不知道它的适用范围到底有多大。
不可能要求智能指针的行为与普通指针一样,另外,如果在运用智能指针的过程中出现了二义性就只能使用C++强制类型转换了。总体上来讲它是利大于弊的。
原则28_4:智能指针与const
在声明普通指针的时候const与
的相对顺序决定了该指针的性质。Const在 的左边说明该指针所指之物是个const不能改变,const在右边说明该指针是const常量一旦被赋值就不能改赋他值。
那么本原则所讨论的就是如何让智能指针也具有次性质。
在正常的智能指针左边加const只能相当于在普通指针的情况下,

const在 右边的情况即智能指针本身是const的,但所指之物并不一定不能被改动。
而下面这种形式的智能指针却能起到在普通指针情况下const在
左边的效果,

即智能指针本身并不是const常量,但是所指之物却是const常量。
那么本原则所要提出的问题来了。在普通指针情况下,允许用非const指针给const指针初始化,但是这在智能指针情况下却不行,例如下面情况。

现在是把一个SmartPtr类型赋值给SmartPtr类型,这在C++看来是俩完全不同的类型。如同原则28_3所使用的方法一样,这种情况也可以通过添加隐式类型转换函数成员模版来解决,从理论上讲只要内建指针能做到的事情,通过这种办法也能做到。
书中提到const与非const之间的转换是单向的,即非const可以转换成const,但是const转换成非const却是不安全的。而且const能做的事情非const都能做到。这符合IS-A的设计原则于是可以用一个const智能指针类作基类一个非const智能指针类作子类去继承const智能指针类。
这样const智能指针类中包含有const指针,非const智能指针类中包含有非const指针,所以这个继承体系中总共有俩内建指针。
不过,这个设计的空间利用率不高,于是可以用UNION来装载俩指针并置于const智能指针类之中。

多谢捧场

如果您觉得我的文章有价值,那么赏脸打赏一个,鄙人感激不尽。不过,不打赏看看也是好的,如果有不对的地方,还请您多多指正。


原则28:智能指针_第2张图片

你可能感兴趣的:(原则28:智能指针)