[C++之泛型]
泛型之于C++正如一块未经雕琢的璞玉,大家都知道它,但对它的不了解,以及它不起眼的外表,总是让我们将它遗忘在一角。很少有程序员真正完全使用泛型(我认为仅仅应用STL,并不算真正使用泛型技术),究其原因,还是对于泛型有些误解,或是了解不够,因而产生排斥。
泛型从表向上看,确实是通过模板技术来实现,但这并不等于说泛型就是模板技术的代名词。而对于模板技术:很多程序员认识是:它是可以让你少写代码一种方式(那么,宏不是也可以吗?难道你不记得MFC的宏为你带来的噩梦吗?)。在此种理解下,大家甚至不愿意去尝试它(另一个重要的原因是;编译器不能很好支持它的调试,这使得对模板本身就不是很了解的大多数人望而却步)。不过,换一个角度,当你未有深厚的内力,就去冒然修练石壁上的绝世武功,搞不好就会走火入魔,所以,避而远之,也为上策。
那泛型究竟是什么呢,我想:你可以试试将它与面向对象设计技术提到同一高度,继承、多态实现面向对象,而模板技术实现泛型。就这么简单,它是一种设计方法,而非特指某种语法。就好象你从不会把面向对象称做一种语法。
我可以举个简单的经典例子(首先声明,鉴于水平有限,例子并不一定很恰当)。
我们借用讲解面向对象中多态特性的经典示例:画图程序:对了,我们已用CShape将所有点线圆等基本图形抽象出来,当Draw时,运行期时它会动态找到找自已的draw method。现在,我们需要添加一种绘制方式,位图图元绘制(它可以是任意媒介上的位图数据),你可能会说:很简单啊,用继承体系定义它:
Class CBitmap:Public CShape {}
一切不就OK了吗?噢,慢着,CBitmap真的属于CShape吗?如果真要用继承体系,似乎需要改变一下:
Class CGraph {virtual void draw(void)=0;…}
Class CShape:Public CGraph {…}
Class CBitmap:Public CGraph {…}
这样,将原来的引用变为CGraph,一切似乎都已解决,好的,我们再进一步,假设我们需要一块魔法画布,它能够绘制一切。可能是基本图形,或是图元,甚至可能是具有Draw方法的一只正在跳舞的小鸭子(Duck)。嗯,问题来了,我如何处理Duck,Duck的源代码暂时不属于我,并且天知道哪天会不会多出一只会唱歌的Cock。好了,现在是泛型发挥作用的时候了(有人可能认为:这不是template模式吗?可能是吧,那应该是另一回事了)。我们认定会Draw的东东它们属于具有某种特征的不同类型,现在我们用泛型将类型参数化:
首先,我们不再需要看起来很别扭的CGrapth,去掉多余的继承体系(复杂的继承体系并不是一个好方法,设计模式中到处都在告诫你)
Template<class T>Void Canvas::Paint(Vector<T&> vObj)
为什么要规定一定是Vector容器呢,我们完全可以支持其它容器,我们再将容器类型参数化:
Template<class C>Void CCanvas::Paint(C & cObj)
既然都到这儿了,我们再进一步,为什么一定非要是容器呢,难道不能是数组或其它支持迭代的东东呢?
Template<class iter>Void CCanvas::Paint(iter first,iter last)
实现很简单,如下:
for(;first!=last;++first) (*first)->draw();
或是:
for_each(first,last,mem_fun(&iter::value_type::draw) );
注意一个事实,多态意味着运行期决议,而泛型则将决议提前到编译阶段,上例中CBitmap不再利用多态,于是它将在编译时确定调用代码,这样带来的好处,是执行效率的提高(这可能对于应用开发来说,有些微不足道,但如果你在实现某种算法时,那就很有必要了)。
泛型还有不少好东东,比如Function Object(上例中的mem_fun是标准C++提供的一个生成function object的适配),Concepts定义(你可以定义上例中迭代器的类型,使得其应用型更强)等。。。它们使泛型更加完整。我在Java1.5并未见到这些特性 (java并不支持操作符重载,有些特性应该是无法实现的)。
模板编程看起来确实有些复杂,它实际上更适合于制作高效的框架程序。我想对于它的应用,我们其实也不应该趋之若鹫。但如果能好好理解它,当你阅读STL或是Boost的源码时,你才会体会到被折服的喜悦,而不是满头雾水。
<未完>
石头 于2005-06-20