科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件C++/CLI:第一流的CLI语言

C++/CLI:第一流的CLI语言

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本文并不是为了奉承C++/CLI的辉煌,也不是为了贬低其它如C#或者VB.NET等语言,相反,这只是一个非官方的、以一个喜欢这种语言的非微软雇员身份来论证C++/CLI有它的自己的唯一的角色,可作为第一流的.NET编程语言。

作者:朱先忠编译 来源:天极网 2007年11月16日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
5. 托管模板

  也许你对泛型的概念已很清楚了,它帮助你避免进入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();

  这样,当你下次遇到对泛型的贬谤时,你就知道该怎么做了。

  6. 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,可以参考有关资料。
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章