设为首页 加入收藏

TOP

19.8.4 接口强制操作符的实现
2013-10-07 15:07:07 来源: 作者: 【 】 浏览:73
Tags:19.8.4 接口 强制 操作 符的 实现

19.8.4  接口强制操作符的实现

你可能早就忍不住想知道这些强制操作符到底是如何工作的了,那么就让我们来考察一下吧。前面提到的3个接口强制类都是从interface_cast_base模板派生来的,继承方式是受保护(protected)继承。只不过这3者实行继承的具体方式略有不同。interface_cast_noaddref是这样的:

程序清单19.15

  1. template< typename I  
  2.         , typename X = throw_bad_interface_cast_exception 
  3.         > 
  4. class interface_cast_noaddref  
  5.   : protected interface_cast_base<I, noaddref_release<I>, X> 
  6. {  
  7. public:  
  8.   typedef interface_cast_base<. . . >   parent_class_type;  
  9.   typedef I                                     interface_pointer_type;  
  10.   typedef . . .                                 protected_pointer_type;  
  11. public:  
  12.   template <typename J> 
  13.   explicit interface_cast_noaddref(J &j)  
  14.     : parent_class_type(j)  
  15.   {}  
  16.   explicit interface_cast_noaddref(interface_pointer_type pi)  
  17.     : parent_class_type(pi)  
  18.   {}  
  19. public:  
  20.   protected_pointer_type operator -> () const  
  21.   {  
  22.     return static_cast<. . .>(parent_class_type::get_pointer());  
  23.   }  
  24. // 声明但不予实现  
  25. private:  
  26.   . . . // 不可访问的拷贝构造函数与拷贝赋值操作符  
  27. };  

从上面的代码可以看出,interface_cast_noaddref的父类是interface_cast_base的一个特化,特化它的主要参数包括noaddref_release仿函数类以及给定的异常策略类。noaddref_release仿函数负责释放在构造函数中获取的接口,以便确保被强制的COM实例上的引用计数从总体上既未增大也未减小。

interface_cast_noaddref具有模板形式的构造函数,这就允许用户对任何类型的COM接口进行强制。由于这是一个需要一定开销的操作,所以该强制提供了第二个构造函数,它接受强制目标类型的指针,该构造函数的实现简单(高效)地在它接受的指针参数上调用AddRef()。两个构造函数都会转发至基类中与它们对应的构造函数。

另外一个值得注意的方面是,用户只能通过调用该强制的operator->() const方法来访问查询到的接口,这个特性有助于确保该强制的安全性。由于该强制并未提供任何隐式转换操作符,所以编译器就会拒绝如下形式的代码,否则这段代码中潜在的死引用(dead reference)危机就可能发作:

  1. IX *px = interface_cast_noaddref<IX>(py); // 编译错误  
  2. px->SomeMethod(); // 崩溃!不过幸运的是,上边的代码根本不能通过编译  

这是典型的苦行僧式编程(www.cppentry.com):通过编译器跟语言的通力合作来防止人们误用我们的类型。然而,在类似下面的这种场景下,这种做法倒确实令该强制变得难于使用了:
  1. func(IX *px);  
  2. IY *py = . . .;  
  3. func(interface_cast_noaddref<IX>(p)); // 编译错误  

COM规则明确表示:当你将一个接口引用传递给一个函数时,除非该函数在它的语义里面明确表示,否则该函数就不拥有这个接口引用。因此,尽管这里改用interface_cast_addref就能通过编译,但这样做就会留下一个悬挂引用。所以说,interface_cast_noaddref正是我们需要的。

当然,只要有需求,就会有解决之道。如果你刚愎自用的话,你总可以这么写:

  1. IStream *pi = interface_cast_noaddref<IStream, . . .>(p).operator ->();  
  2. pi->SomeMethod(); // 未定义行为!可能崩溃  

但你要是这么做的话,我可就要叫"C++(www.cppentry.com)警察"了,你会遭到这样的责令:"别再编码了!回家去吧,呆上一年再说"。当然,由于不完美主义编程(www.cppentry.com)告诉我们要尊重有经验的开发者的意愿,所以这里有一个解决方案。我们可以使用一个相关联的get_ptr()属性垫片(Attribute Shim,我们将会在下一章涉及垫片概念的方方面面),像这样:
  1. func(get_ptr(interface_cast_noaddref<IX>(p))); // 没问题 

另外两个接口强制的实现类似于interface_cast_noaddref。interface_cast_test使用了"惰性异常"策略类型,即ignore_interface_cast_exception,这样一来即使强制失败也不会导致异常的抛出,而是通过布尔类型的隐式转换操作符来体现。 interface_cast_test使用了noaddref_release来确保COM对象的引用计数既不增也不减。

程序清单19.16

  1. template<typename I> 
  2. class interface_cast_test  
  3.   : protected interface_cast_base<I, noaddref_release<I>,  
  4.                                       ignore_interface_cast_exception> 
  5. {  
  6.   . . .  
  7.   operator bool () const  
  8.   {  
  9.     return NULL != parent_class_type::get_pointer();  
  10.   }  
  11.   . . .  

interface_cast_addref同样使用了"惰性异常"策略,尽管只是作为一个缺省的模板策略参数。不同的是,interface_cast_addref使用了addref_release仿函数来对引用计数进行必要的递增。它还提供了一个隐式转换操作符,以便让用户访问他们请求的接口。

程序清单19.17

  1. template< typename I  
  2.         , typename X = ignore_interface_cast_exception 
  3.         > 
  4. class interface_cast_addref  
  5.     : protected interface_cast_base<I, addref_release<I>, X> 
  6. {  
  7.   . . .  
  8.   operator pointer_type () const  
  9.   {  
  10.     return parent_class_type::get_pointer();  
  11.   }  
  12.   . . .  

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇19.8.8 interface_cast 尾声 下一篇19.8.7 IID_traits

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: