技术开发 频道

C++/CLI:第一流的CLI语言(4)

【IT168 技术文档】

  托管模板
 
  也许你对泛型的概念已很清楚了,它帮助你避免进入C++的模板梦魇,它是实现模板的非常好的方式,等等。好,假设这些全部正确,C++/CLI支持泛型就象任何其它CLI语言一样-但是它有而其它一些CLI语言还没有的是它还支持托管模板-也就是模板化的ref和value类。如果你以前从未使用过模板,你不能一下欣赏这么多优点,但是如果你有模板使用背景而且你已发现了泛型中存在的可能限制你编码的方式,托管模板将会大大减轻你的负担。你能联合使用泛型和模板- 事实上有可能用一个托管类型的模板参数来实例化一个泛型类型(尽管相反的情形是不可能的,因为运行时刻实例化由泛型所用)。STL.net (或STL/CLR)以后讨论,请很好地利用泛型和托管模板的混合编程吧。

  泛型使用的子类型约束机制将防止你写出下面的代码:

generic<typename T> 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<typename T> T Add(T t1, T t2) { return t1 + t2; }
  那么就可以这样做:
int x1 = 10, x2 = 20; int xsum = Add<int>(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<R^>(r1, r2);
  这在一个象int的本机类型以及一个ref类型(只要ref类型有一个+运算符)情况下都能工作良好。这个泛型缺点不是一个调试错误或缺陷-它是设计造成的。泛型的实例化是在运行时通过调用配件集实现的,因此编译器不能确知一特定操作能被施行于一个泛型参数,除非它匹配一个子类型约束,因此编译器在定义泛型时解决这个问题。当你使用泛型时的另外一个妨碍是,它不会允许你使用非类型参数。下列泛型类定义不会编译:
generic<typename T, int x> ref class G{};
  编译错:
 
error C2978: syntax error : expected typename or class; found type int; non-type parameters are not supported in generics
 
  与托管模板相比较:
template<typename T, int x = 0> ref class R{};
  如果你开始感激C++向你提供了泛型和托管模板,那么请看下面这一个例子:
template<typename T> ref class R{ public: void F() { Console::WriteLine("hey"); } }; template<> ref class R<int> { public: void F() { Console::WriteLine("int"); } };
  你不能用泛型这样编码;否则,将产生:
 
  编译错:error C2979: explicit specializations are not supported in generics
 
  但可以在继承链中混合使用模板和泛型:
generic<typename T> ref class Base { public: void F1(T){} }; template<typename T> ref class Derived : Base<T> { public: void F2(T){} }; //... Derived<int> d; d.F1(10); d.F2(10);
  最后,你不能从一个泛型参数类型派生一个泛型类。
 
  下列代码不会成功编译:
 
generic<typename T> ref class R : T
{};
 
  error C3234: a generic class may not derive from a generic type parameter
 
  模板让你这样做(好像你还不知道这些):
ref class Base{ public: void F(){} }; generic<typename T> ref class R : T {}; //... R<Base> r1; r1.F();
  这样,当你下次遇到对泛型的贬谤时,你就知道该怎么做了。

 

  STL/CLR

  当大量使用STL的C++开发者转向.NET1/1.1时一定感觉非常别扭,他们中的许多可能会放弃并转回到原来的本机编码。从技术上讲,你能结合.NET类型(using gcroot)使用本机STL,但是产生的结果代码可能相当低效,更不用说是丑陋了:

std::vector< gcroot<IntPtr> >* m_vec_hglobal; //... for each(gcroot<IntPtr> 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<String^> vecstr; vecstr.push_back("wally"); vecstr.push_back("nish"); vecstr.push_back("smitha"); vecstr.push_back("nivi"); deque<String^> 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<typename ForwardIterator> 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<String^>::iterator it = vecstr.begin(); it < vecstr.end(); it++) Console::WriteLine(*it); Console::WriteLine(); for(deque<String^>::iterator it = deqstr.begin(); it < deqstr.end(); it++) Console::WriteLine(*it);
  上面我的算法能够与vector和deque容器工作良好。至此,不再细谈;否则,guru站上的STL爱好者们会对我群起攻击,而非STL人可能感到厌烦。如果你还没使用过STL,可以参考有关资料。

 

0
相关文章