5. 托管模板也许你对泛型的概念已很清楚了,它帮助你避免进入C++的模板梦魇,它是实现模板的最佳方式,等等。好,假设这些全部正确,C++/CLI支持泛型就象任何其它CLI语言一样-但是它有而其它一些CLI语言还没有的是它还支持托管模板-也就是模板化的ref和value类。如果你以前从未使用过模板,你不能一下欣赏这么多优点,但是如果你有模板使用背景而且你已发现了泛型中存在的可能限制你编码的方式,托管模板将会大大减轻你的负担。你能联合使用泛型和模板- 事实上有可能用一个托管类型的模板参数来实例化一个泛型类型(尽管相反的情形是不可能的,因为运行时刻实例化由泛型所用)。STL。net(或STL/CLR)以后讨论,请很好地利用泛型和托管模板的混合编程吧。上面的内容是达内老师接着第一流的CLI语言第一部分内容继续为大家分享的,希望大家能够关注我们。泛型使用的子类型约束机制将防止你写出下面的代码:generic T Add(T t1, T t2){ return t1 + t2; }编译错误:error C2676: binary ’+’ : ’T’ does not define this operator or a conversion to a type acceptable to the predefined operator现在请看相应的模板版本:template T Add(T t1, T t2){ return t1 + t2; }那么就可以这样做:int x1 = 10, x2 = 20;int xsum = Add(x1, x2);还可以这样做:ref class R{int x;public:R(int n):x(n){}R^ operator+(R^ r){ return gcnew R(x + r->x); }};//...R^ r1 = gcnew R(10);R^ r2 = gcnew R(20);R^ rsum = Add(r1, r2);这在一个象int的本机类型以及一个ref类型(只要ref类型有一个+运算符)情况下都能工作良好。这个泛型缺点不是一个调试错误或缺陷-它是设计造成的。泛型的实例化是在运行时通过调用配件集实现的,因此编译器不能确知一特定操作能被施行于一个泛型参数,除非它匹配一个子类型约束,因此编译器在定义泛型时解决这个问题。当你使用泛型时的另外一个妨碍是,它不会允许你使用非类型参数。下列泛型类定义不会编译:generic ref class G{};编译错:error C2978: syntax error : expected ’typename’ or ’class’; found type ’int’; non-type parameters are not supported in generics与托管模板相比较:template ref class R{};如果你开始感激C++向你提供了泛型和托管模板,那么请看下面这一个例子:template ref class R{public:void F(){ Console::WriteLine('hey'); }};template<> ref class R{public:void F(){ Console::WriteLine('int'); }};你不能用泛型这样编码;否则,将产生:编译错:error C2979: explicit specializations are not supported in generics但可以在继承链中混合使用模板和泛型:generic ref class Base{public:void F1(T){}};template ref class Derived : Base{public:void F2(T){}};//...Derived d;d.F1(10);d.F2(10);最后,你不能从一个泛型参数类型派生一个泛型类。下列代码不会成功编译:generic ref class R : T{};error C3234: a generic class may not derive from a generic type parameter模板让你这样做(好像你还不知道这些):ref class Base{public:void F(){}};generic ref class R : T{};//...R r1;r1.F();这样,当你下次遇到对泛型的贬谤时,你就知道该怎么做了。6. STL/CLR当大量使用STL的C++开发者转向.NET1/1.1时一定感觉非常别扭,他们中的许多可能会放弃并转回到原来的本机编码。从技术上讲,你能结合.NET类型(using gcroot)使用本机STL,但是产生的结果代码可能相当低效,更不用说是丑陋了:std::vector< gcroot >* m_vec_hglobal;//...for each(gcroot ptr in *m_vec_hglobal){ Marshal::FreeHGlobal(ptr);}大概VC++小组考虑到了这些并决定在Whidbey以后,他们会提供STL.NET(或STL/CLR)并可以单独从网上下载。你可能问为什么?Stan Lippman,在他的MSDN文章(STL.NET Primer)中给出了3条原因:·可扩展性--STL设计把算法和容器隔离到自己的应用空间-也就是你可以有一组容器和一组算法,并且你能在任何一个容器上使用这些算法;同时你能在任何一个算法中使用这些容器。因此,如果你添加一种新的算法,你能在任何一种容器中使用它;同样,一个新的容器也可以与现有算法配合使用。·统一性--所有核心C++开发者集中在一起,汇集起他们精妙的STL专长,再使用他们的专长则轻车熟路。要较好地使用STL需要花费时间-然而一旦你掌握了它,你就有了在.NET世界中使用你的技巧的明显优势。不是吗?·性能--STL.NET通过使用实现泛型接口的托管模板实现。并且既然它的核心已用C++和托管模板编码,可以期盼它比在BCL上使用的泛型容器更具有性能优势。使用过STL的人不需要任何示范,所以下面代码有益于以前没有使用过STL的人。vector vecstr;vecstr.push_back('wally');vecstr.push_back('nish');vecstr.push_back('smitha');vecstr.push_back('nivi');deque deqstr;deqstr.push_back('wally');deqstr.push_back('nish');deqstr.push_back('smitha');deqstr.push_back('nivi');我使用了两个STL.NET容器-vector和deque,并装满两个容器,使其看起来相同(在两个容器中都使用了push_back)。现在,我将在两个容器上使用replace算法-我们再次看到,这些代码是很相同的。replace(vecstr.begin(), vecstr.end(),gcnew String('nish'), gcnew String('jambo'));replace(deqstr.begin(), deqstr.end(),gcnew String('nish'), gcnew String('chris'));这里特别要注意的是我使用了'同样'的算法--replace并在两个不同STL容器上使用相同的函数调用。这是当Stan谈及'可扩展性'时的意思。下面我用一个简单函数来证明:template void Capitalize(ForwardIterator first,ForwardIterator end){for(ForwardIterator it = first; it < end; it++)*it = (*it)->ToUpper();}它遍历一个System::String^容器并把其中的每个字符串转化为大写。Capitalize(vecstr.begin(), vecstr.end());Capitalize(deqstr.begin(), deqstr.end());for(vector::iterator it = vecstr.begin();it < vecstr.end(); it++)Console::WriteLine(*it);Console::WriteLine();for(deque::iterator it = deqstr.begin();it < deqstr.end(); it++)Console::WriteLine(*it);上面我的算法能够与vector和deque容器工作良好。至此,不再细谈;否则,guru站上的STL爱好者们会对我群起攻击,而非STL人可能感到厌烦。如果你还没使用过STL,可以参考有关资料。7. 熟悉的语法开发者经常迷恋他们所用的编程语言,而很少是出于实用的目的。还记得当微软宣布不再为VB6提供官方支持时,VB6人的反抗吗?非VB6人对此可能非常震惊,而老道的VB6人早已为他们的语言作好葬礼准备了。事实上,如果VB.net从来没被发明,多数VB6人将会离开.net,因为C#将会对他们非常陌生,而它的祖先就是C++。如今,许多VB.NET人可能已经转向了C#,但是他们不会从VB6直接转向C#;VB.NET起到一个桥梁作用让他们的思想脱离开原来VB6思想。相应地,如果微软仅发行VB.NET(而没有C#),那么.NET可能成为了新的面向对象VB,且带有一个更大的类库-C++社团的人可能对此嗤之以鼻-他们甚至不会麻烦地检验.NET基础类库。为什么任何使用一种特定语言的开发者会对另外一个团体的使用另外开发语言的开发者嗤之以鼻?这不是我要回答的问题。--要回答该问题也许要先回答为什么有的人喜欢威士忌,有的人喜欢可口可乐,而还有人喜欢牛奶。所有我要说的是,对开发者来说,语法家族是个大问题。你认为对于一个具有C++背景的人,下面的代码具有怎样的直觉性?char[] arr =new char[128];他/她可能回答的第一件事是,方括号放错了位置。下面这句又如何?int y=arr.Length;'呀!'-最可能的反映。现在把下面与前面相比较:char natarr[128];array^ refarr=gcnew array(128);int y=refarr->Length;请注意声明一个本机数组和一个托管数组时的语法区别。这里不同的模板形式的语法可视化地告诫开发者这一事实--refarr并不是典型的C++数组而且它可能是某种CLI类的派生物(事实上确是如此),所以极有可能可以把方法和属性应用于它。C#的finalizer语法最有可能引起转向C#的C++程序员的混淆。请看见下列C#代码:class R{ ~R(){} }好,这样~R看起来象一个析构器但实际是个finalizer。为什么?请与下面的C++代码比较:ref class R{~R(){ }!R(){ }};这里~R是析构器(实际上等价于一个析构器的Dispose模式-但对C++人员来说,这它的行为象个析构器)而新的!R语法是为finalizer建立的-这样就不再有混淆而且语法看上去也与本机C++相匹配。请看一下C#泛型语法:class R{};再请看一下C++的语法:generic ref class R{};曾经使用过模板的人马上就看出这种C++语法,而C#语法不能保证其没有混淆性且也不直观。我的观点是,如果你以前具有C++背景,C++/CLI语法将最贴近于你以前所用。C#(以及J#)看上去象C++,但是还有相当多的极为使人烦火的奇怪语义差别而且如果你没有完全放弃C++,语法差别将永远不停地带给你混乱和挫折。从这种意义上说,我认为VB.NET更好些,至少它有自己唯一的语法,所以那些共用C++和VB.NET的人不会产生语法混乱。8、最后,至于你用什么语言编程,这可能依赖许多因素——如:你在学校学习的是什么语言,你是用什么语言开发的现有代码仓库,是否你的客户对你有具体的语言要求等。本文的主要目的是帮助你确定使用C++/CLI的几个明确的场所--这里,它比另外CLI语言更具有明显优势。如果你开发的应用程序有90%的使用时间是涉及本机interop,为何还要考虑使用另外的而不是C++?如果你想开发一个通用集合,为什么仅把你自己限制在泛型上,而不是结合泛型和模板的优势呢?如果你已经用C++工作,为什么还要学习一种新的语言?我常觉得象C#和VB.NET这样的语言总是尽量向开发者隐藏CLR,而C++不仅让你品味CLR,甚至可以让你去亲吻CLR!
下一篇:绝缘体导体检测器