﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-deepway-文章分类-C++ 设计模式</title><link>http://www.cppblog.com/maxime/category/14144.html</link><description /><language>zh-cn</language><lastBuildDate>Mon, 28 Jun 2010 12:42:13 GMT</lastBuildDate><pubDate>Mon, 28 Jun 2010 12:42:13 GMT</pubDate><ttl>60</ttl><item><title>从 WebKit 源代码看“类型识别”</title><link>http://www.cppblog.com/maxime/articles/118884.html</link><dc:creator>maxime</dc:creator><author>maxime</author><pubDate>Mon, 28 Jun 2010 12:21:00 GMT</pubDate><guid>http://www.cppblog.com/maxime/articles/118884.html</guid><wfw:comment>http://www.cppblog.com/maxime/comments/118884.html</wfw:comment><comments>http://www.cppblog.com/maxime/articles/118884.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/maxime/comments/commentRss/118884.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/maxime/services/trackbacks/118884.html</trackback:ping><description><![CDATA[<p><br>问题描述：<br>&nbsp;&nbsp;&nbsp;在 C++ 面向对象编程中，常常会遇到&#8220;类型识别&#8221;的需求：已知某个对象的基类型指针，需要识别该对象的派生类型，访问派生类型的公有函数。简单说就是，将基类型指针转换为派生类型指针，并用来访问派生类型的公有函数。<br><br>问题分析：&nbsp;<br>&nbsp;&nbsp;&nbsp;&#8220;类型识别&#8221;本质上是一个从抽象转为具象的过程，破坏了对象抽象性。虽不值得提倡，但在具象化情景下，&#8220;类型识别&#8221;又是必须的。<br>&nbsp;&nbsp;&nbsp;&#8220;类型识别&#8221;常规方法是强制类型转换。这需要先定义一个&#8220;对象类型&#8221;枚举表，再在基类里定义一个返回&#8220;对象类型&#8221;的函数。这种方法的缺点显而易见：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1. 需要维护全局性的&#8220;对象类型&#8221;枚举表；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2. 向基类型构造函数传入&#8220;对象类型&#8221;参数；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3. 每次&#8220;类型转换&#8221;前，先要获得&#8220;对象类型&#8221;值，判断对象类型；<br>&nbsp;&nbsp;&nbsp;&#8220;类型识别&#8221;的另一种标准方法是RTTI。作为一项 C++ 编译选项，RTTI 直接将上述常规方法教给编译器做了。然而，这样也是有缺点的：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1. RTTI会引入额外的开销。这是可以理解的，问题是RTTI这种开销会附加到所有类型上去；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2. RTTI要求所有库都以RTTI方式编译，才能正常工作。而现实世界总是复杂的，这一点未必总是能实现；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.&nbsp;个人认为RTTI是一种过于形式化的东西，违反了 C++ 的简洁性、高效性原则；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4. 个人认为RTTI的低层形式化的东西，很多时候，我们仍然需要定义不依赖于C++类的&#8220;对象类型&#8221;枚举表；<br>&nbsp;&nbsp;&nbsp;阅读 WebKit 代码时，我看到了一种基于虚函数的&#8220;类型识别&#8221;方法，代码如下：<br>&nbsp;&nbsp;&nbsp;class EventTarget {<br>&nbsp;&nbsp;&nbsp;public:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void ref() { refEventTarget(); }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void deref() { derefEventTarget(); }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual EventSource* toEventSource();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual MessagePort* toMessagePort();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual Node* toNode();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual DOMWindow* toDOMWindow();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual XMLHttpRequest* toXMLHttpRequest();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual XMLHttpRequestUpload* toXMLHttpRequestUpload();</p>
<p>&nbsp;在基类中，定义向派生类型的转换函数，并且返回为NULL。每个派生类型，重新实现向自身转换的转换函数，返回自身的指针。我感觉这种方法：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1. 非常安全，使用方便；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2. 效率相对比较高；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3. 省却了全局性的&#8220;对象类型&#8221;枚举表，却带来了维护基类&#8220;转换函数&#8221;的负担；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4. 造成了基类对派生类的依赖性，似乎不太好。<br><br>&nbsp;&nbsp;&nbsp;在上述方法的基础上，设想了一种改进的方法，设计原则是&#8220;从基类型向派生类型转换的函数，应该放置在派生类中，以静态函数的方式定义&#8221;。具体方法如下：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1. 基类定义 void* toXXXX()&nbsp;和 bool isXXXX() 两个私有虚函数，用编程规范规定，禁止派生类直接访问它们。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2. 派生类重定义上述函数。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3. 派生类型定义 XXXX* toType(Base*) 和 bool isType(Base*)两个静态函数，内部实现就是通过上述私有虚函数完成的。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4. 所有的&#8220;类型识别操作&#8221;都使用上述静态函数完成。<br></p>
问题小结：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上述四种方式，很难说哪一种方式更好，宜具体情况具体对待。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我本人偏向于，使用最后一种方式作为C++编程规范和模式，因为，它更是一种接口规范，其内部实现可以改为前三种方法中的任何一种。至于它带来的些许工作量，我认为，在它成为一种工作规范后，那些少量代码仅仅只是一点点没有难度的机械性编程，完全可以忽略不计。如果不介意在代码中使用宏机制，那么更可以用宏来实现，那么只需定义编译宏就能就在三种方式之间任意切换。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最后一种方式，体现了一种策略：当你想为基类定义一些与派生类相关的工具函数时，倒不如将其定义为派生类的静态函数，以避免派生类型对基类型的&#8220;污染&#8221;。<br><br>
<img src ="http://www.cppblog.com/maxime/aggbug/118884.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/maxime/" target="_blank">maxime</a> 2010-06-28 20:21 <a href="http://www.cppblog.com/maxime/articles/118884.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>