【M28】智能指针

1、什么是智能指针?

  所谓智能指针就是,看起来,用起来,感觉起来都像原始指针,但是提供了更多功能。

2、使用智能指针取代原始指针,可以获得更多的控制权。如下:

  a、在构造和析构的时候,可以做一些事。

  b、在复制和赋值的时候,可以做一些事。

  c、在解引用的时候,可以做一些事。

3、智能指针可以对不同类型的指针进行封装,因此智能指针是一个模板类。

4、智能指针对原始指针封装,内含一个原始指针,为了用起来像指针,重载->和*,->返回原始指针,*返回对象的引用,两个重载操作符都不改变智能指针,因此是const成员方法。

5、考虑auto_ptr,拥有权转移,auto_ptr的copy构造会修改rhs,因此rhs不是const T&,而是T&。C++默认是传值,导致拥有权转移,如果期望拥有权不转移,使用传引用。

6、解引用操作符*,必须返回引用,不能返回对象。因为解引用操作符,可能返回T的子类。如果返回对象,首先导致对象复制,其次可能照成对象切割,失去多态功能。

7、判断智能指针是否为NULL。对于原始指针,使用if(pa == null),但是对于智能指针,不能这样使用,因为智能指针是个对象。怎么解决这个问题呢?

  a、增加isNull方法,缺点是与原始指针的用法不一致。
  b、为了与原始指针的用法一致,增加隐式类型转换操作符,operator void*()。但是这会导致一个问题,任意两个智能指针,都能进行比较,因为他们都可以隐式转化为void*,这不是我们所期望的。

8、智能指针如何暴露出原始指针?

  使用&*pa,合法,先取引用,再取地址。但是这种写法太难看了。增加隐式类型转换操作符operator T*(),这会引入一个问题,不是我们期望的转换,编译器也会偷偷进行。使用get方法,返回原始指针,避免编译器偷偷地进行转换。
注意:暴露原始指针是危险的,因为暴露出去的原始指针,不再受控。比如:智能指针把资源delete了,外面的原始指针就是野指针了。或者外面的指针执行了delete,智能指针管理的指针指向垃圾。

9、考虑继承,C++是强制类型的。Smart_Ptr<Base>与Smart_Ptr<Derived>是两个完全不同的类,之间没有任何关系,更谈不上继承。那么问题来了,对于原始指针可以指向子类对象,而智能指针不行。如下:
  Base* b = new Derived(); // OK
  Smart_Ptr<Base> b = Smart_Ptr<Derived>(new Derived); //Error
  编译失败,这不合理,怎么解决这个问题呢?

10、首先想到的是,隐式类型转换。这显然不合理,对原始指针类型的所有转换都要考虑。

11、有没有更好的解决办法呢?

使用C++语言的新特性,成员方法模版。隐式类型转换操作符声明为成员方法模版,如下:
template <typename NewType>
operator Smart_Ptr<NewType>()
{
  return Smart_Ptr<NewType>(this->ptr);
}
考虑Smart_Ptr<Base> b = Smart_Ptr<Derived>(new Derived)的执行过程:
  a、编译器首先看看Smart_Ptr<Base>有没有一个构造方法,该构造方法只需要一个形参,形参类型为Smart_Ptr<Derived>,没有找到;
  b、编译器看看Smart_Ptr<Derived>有没有隐式类型转换操作符,将Smart_Ptr<Derived>转换为Smart_Ptr<Base>,没有找到;
  c、编译器尝试将成员方法模版实例化出来一个合适的方法,也就是一个隐式类型转换操作符,使得隐式类型转换成功。
将NewType实例化为模版实参Base,在return语句的构造方法中,使用原始指针Derived,初始化Base指针,构造出Smart_Ptr<Base>,当然可以。
  注意,这里还放大了权限,只要NewType指针可以转换为T指针,那么Smart_Ptr<NewType>对象就可以赋值给Smart_Ptr<T>对象。
  成员方法模版,还有两个问题。首先,考虑fun(Base&),fun(Derived&),对于Derived derived1, fun(derived1)的调用,fun(Derived&)的匹配程度更高。但是,对于成员方法模版,都可实例出一个方法,使得调用成功,并且匹配程度是一样的,这就导致不确定的调用,编译错误。其次,成员方法模版移植性不高,有些编译器不支持。

12、智能指针与const
  C++设计理念是,最小特权原则。让别人做一件事,尽量限制他的权利。一个典型的例子就是,C++在传递引用过程中,可以缩小权力,但不能放大权利。
  原始指针与const有两种关系:a、指向const对象的指针,不能修改指向的对象;b、指针常量,不能修改指向。因此,对于智能指针,我们也希望有这种弹性。但是,对于智能指针只有一个地方可以const,修饰智能指针对象,也就相当于修饰指针。那怎么样修饰指向的对象呢?使用Smart_Ptr<const T>。那么问题来了,Smart_Ptr<T> 与Smart_Ptr<const T>是两个完全不同的类型,当然不能赋值。该怎么办呢?不要再想着隐式类型转换了,因为隐式类型转换会导致非预期的转换。考虑里氏代换,子类能力更大。从const T 到T,是能力变大的过程,因此可以考虑Smart_Ptr<T>: Smart_Ptr<const T>

posted on 2014-04-03 19:48  Andy Niu  阅读(578)  评论(0编辑  收藏  举报