条款28:灵巧(smart)指针(下)
译者注:由于我无法在文档区贴上图片(在论坛询问,结果无人回答),所以只能附上此译文的word文档。.NET/FileBBS/files/2001_12/T_967_1.zip">下载
这种技术能给我们几乎所有想要的行为特性。假设我们用一个新类CasSingle来扩充MusicProduct类层次,用来表示cassette singles。修改后的类层次看起来象这样:XML:namespace prefix = o ns = "urn:schemas-microsoft-com:Office:office" />
ASPectratio="t" v:ext="edit">
现在考虑这段代码:
template class SmartPtr { ... }; // 转换操作符的成员模板 void displayAndPlay(const SmartPtr int howMany); void displayAndPlay(const SmartPtr int howMany); SmartPtr displayAndPlay(dumbMusic, 1); // 错误! 在这个例子里,displayAndPlay被重载,一个函数带有SmartPtr 通过成员模板来实现灵巧指针的类型转换有还有两个缺点。第一,支持成员模板的编译器较少,所以这种技术不具有可移植性。以后情况会有所改观,但是没有人知道这会等到什么时候。第二,这种方法的工作原理不很明了,要理解它必须先要深入理解函数调用的参数匹配,隐式类型转换函数,模板函数隐式实例化和成员函数模板。有些程序员以前从来没有看到过这种技巧,而却被要求维护使用这种技巧的代码,我真是很可怜他们。这种技巧确实很巧妙,这自然是肯定,但是过于的巧妙可是一件危险的事情。 不要再拐弯抹角了,直接了当地说,我们想要知道的是在继承类向基类进行类型转换方面,我们如何能够让灵巧指针的行为与dumb指针一样呢?答案很简单:不可能。正如Daniel Edelson所说,灵巧指针固然灵巧,但不是指针。最好的方法是使用成员模板生成类型转换函数,在会产生二义性结果的地方使用casts(参见条款2)。这不是一个完美的方法,不过也很不错,在一些情况下去除二义性,所付出的代价与灵巧指针提供复杂的功能相比还是值得的。 灵巧指针和const 对于dumb指针来说,const既可以针对指针所指向的东西,也可以针对于指针本身,或者兼有两者的含义(参见Effective C++条款21): CD goodCD("Flood"); const CD *p; // p 是一个non-const 指针 //指向 const CD 对象 CD * const p = &goodCD; // p 是一个const 指针 // 指向non-const CD 对象; // 因为 p 是const, 它 // 必须被初始化 const CD * const p = &goodCD; // p 是一个const 指针 // 指向一个 const CD 对象 我们自然想要让灵巧指针具有同样的灵活性。不幸的是只能在一个地方放置const,并只能对指针本身起作用,而不能针对于所指对象: const SmartPtr &goodCD; // 指向 non-const CD 对象 好像有一个简单的补救方法,就是建立一个指向cosnt CD的灵巧指针: SmartPtr &goodCD; // 指向const CD 对象 现在我们可以建立const和non-const对象和指针的四种不同组合: SmartPtr // non-const 指针 SmartPtr // non-const 指针 const SmartPtr // const指针 const SmartPtr // const 指针 但是美中不足的是,使用dumb指针我们能够用non-const指针初始化const指针,我们也能用指向non-cosnt对象的指针初始化指向const对象的指针;就像进行赋值一样。例如: CD *pCD = new CD("Famous Movie Themes"); const CD * pConstCD = pCD; // 正确 但是如果我们试图把这种方法用在灵巧指针上,情况会怎么样呢? SmartPtr SmartPtr SmartPtr 包括const的类型转换是单向的:从non-const到const的转换是安全的,但是从const到non-const则不是安全的。而且用const指针能的事情,用non-const指针也能做,但是用non-const指针还能做其它一些事情(例如,赋值操作)。同样,用指向const的指针能做的任何事情,用指向non-const的指针也能做到,但是用指向non-const的指针能够完成一些使用指向const的指针所不能完成的事情(例如,赋值操作)。 这些规则看起来与public继承的规则相类似(Effective C++ 条款35)。你能够把一个派生类对象转换成基类对象,但是反之则不是这样,你对基类所做的任何事情对派生类也能做,但是还能对派生类做另外一些事情。我们能够利用这一点来实作灵巧指针,就是说可以让每个指向T的灵巧指针类public派生自一个对应的指向const-T的灵巧指针类: template class SmartPtrToConst { // 灵巧指针 ... // 灵巧指针通常的 // 成员函数 protected: union { const T* constPointee; // 让 SmartPtrToConst 访问 T* pointee; // 让 SmartPtr 访问 }; }; template class SmartPtr: // 的灵巧指针 public SmartPtrToConst ... // 没有数据成员 }; 使用这种设计方法,指向non-const-T对象的灵巧指针包含一个指向const-T的dumb指针,指向const-T的灵巧指针需要包含一个指向cosnt-T的dumb指针。最方便的方法是把指向const-T的dumb指针放在基类里,把指向non-const-T的dumb指针放在派生类里,然而这样做有些浪费,因为SmartPtr对象包含两个dumb指针:一个是从SmartPtrToConst继承来的,一个是SmartPtr自己的。 一种在C世界里的老式武器可以解决这个问题,这就是union,它在C++中同样有用。Union在protected中,所以两个类都可以访问它,它包含两个必须的dumb指针类型,SmartPtrToConst 利用这种新设计,我们能够获得所要的行为特性: SmartPtr SmartPtrToConst 评价 有关灵巧指针的讨论该结束了,在我们离开这个话题之前,应该问这样一个问题:灵巧指针如此繁琐麻烦,是否值得使用,特别是如果你的编译器缺乏支持成员函数模板时。 经常是值得的。例如通过使用灵巧指针极大地简化了条款29中的引用计数代码。而且正如该例子所显示的,灵巧指针的使用在一些领域受到极大的限制,例如测试空值、转换到dumb指针、继承类向基类转换和对指向const的指针的支持。同时灵巧指针的实作、理解和维护需要大量的技巧。Debug使用灵巧指针的代码也比Debug使用dumb指针的代码困难。无论如何你也不可能设计出一种通用目的的灵巧指针,能够替代dumb指针。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/10797429/viewspace-101621/ ,如需转载,请注明出处,否则将追究法律责任。