科技行者

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

知识库

知识库 安全导航

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

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

  • 扫一扫
    分享文章到微信

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

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

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

关键字:

  • 评论
  • 分享微博
  • 分享邮件
4. 混合类型

  我们知道,C++支持本机类型-总是如此;C++支持CLI类型-本文正是特别强调这一点;它还支持混合类型-具有CLI成员的本机类型和具有本机成员的CLI类型!请尽管考虑所有你能的可能需求。

  注意,谈到Whidbey,混合类型实现还不完整;就我从Brandon,Herb和Ronald发表的材料的理解得知,存在这种相当酷的类型--统一模型,它将在Orcas中实现--你能够在本机C++堆上new/delete CLI类型,而且也能够在CLI堆上gcnew/delete本机类型。但既然这是Whidbey以后的东西,本文不讨论统一模型。

  在我谈论你何时使用混合类型以前,我想向你说明什么是混合类型。如果你理解混合类型,请跳过下面几段。这里引用Brandon Bray的说法:"一种混合类型,或者是本机类ref类(需要有对象成员),或者是通过声明或继承被分配在垃圾回收堆或本机堆上的。"因此如果你有一个托管类型或者有一个有托管成员的本机类型,你就有了一个混合类型。VC++ Whidbey不直接支持混合类型(统一类型模型是一种Whidbey之后的概念),但是它给我们划定了实现混合类型的条件。让我们开始讨论包含托管成员的本机类型。

ref class R
{
public:
void F(){}
//假定 non-trivial ctor/dtor
R(){}
~R(){}
};

  在我的例子中,设想该托管类型R有一个non-trivial构造器和一个non-trivial析构器。

class Native
{
private:
gcroot<R^> m_ref;
public:
Native():
m_ref(gcnew R()){}
~Native()
{ delete m_ref; }
void DoF()
{ m_ref->F(); }
};

  既然,我不能在我的类中拥有一个R成员,我使用了gcroot模板类(在gcroot.h中声明,但是你要用"#include vcclr.h"),它包装了System::Runtime::InteropServices::GCHandle结构。它是个象类一样的灵敏指针,它重载了运算符->以返回用作模板参数的托管类型。因此在上面类中,我可以使用m_ref,就好象我已经声明它是R^,而且你能在DoF函数中看到这正在起作用。实际上你可以节省delete,这可以通过使用auto_gcroot(类似于std::auto_ptr,在msclr\auto_gcroot.h文件中声明)代替gcroot来实现。下面是一个更好些的使用auto_gcroot的实现。

class NativeEx
{
private:
msclr::auto_gcroot<R^> m_ref;
public:
NativeEx() : m_ref(gcnew R()){}
void DoF()
{ m_ref->F(); }
};

  下面让我们看相反的情形:一个CLI类的本机成员。

ref class Managed
{
private:
Native* m_nat;
public:
Managed():m_nat(new Native()){ }
~Managed()
{ delete m_nat; }
!Managed()
{ delete m_nat;
#ifdef _DEBUG
throw gcnew Exception("Oh, finalizer got called!");
#endif
}
void DoF()
{ m_nat->DoF(); }
};

  我不能定义一个Native对象来作为一个ref类成员,因此需要使用一个Native*对象来代替。我在构造器中new该Native对象,然后在析构器和finalizer中delete它。如果你运行该工程的调试版,在执行到finalizer时将抛出一个异常- 因此开发者可以马上添加一个对delete的调用或为他的CLI类型使用栈语义技术。奇怪的是,库开发小组没有建立一个gcroot的反向实现-但这不是个大问题,我们可以自己写。

template<typename T> ref class nativeroot
{
T* m_t;
public:
nativeroot():m_t(new T){}
nativeroot(T* t):m_t(t){}
T* operator->()
{ return m_t; }
protected:
~nativeroot()
{ delete m_t; }
!nativeroot()
{
delete m_t;
#ifdef _DEBUG
throw gcnew Exception("Uh oh, finalizer got called!");
#endif
}
};

  这仅是个相当简单的灵敏指针实现,就象一个负责本机对象分配/回收的ref类。不管怎样,借助nativeroot模板类,我们可以如下修改托管类:

ref class ManagedEx
{
private:
nativeroot<Native> m_nat;
public:
void DoF()
{ m_nat->DoF(); }
};

  好,关于混合类型的最大问题是什么呢?你可能问。最大问题是,现在你能混合使用你的MFC、ATL、WTL、STL代码仓库和.NET框架,并用可能的最直接的方式-只需写你的混合模式代码并编译实现!你可以建立在一个DLL库中建立MFC 类,然后建立一个.NET应用程序来调用这个DLL,还需要把.NET类成员添加到你的MFC类(也实现可以相反的情况)。

  作为一例,设想你有一MFC对话框--它通过一个多行的编辑框接受来自用户的数据-现在,你有一新的要求-显示一个只读编辑框,它将显示当前在该多行编辑框中文本的md5哈希结果。你的队友正在悲叹他们将必须花费几个小时钻研crypto API,而你的上司在担忧你们可能必须要买一个第三方加密库;那正是你在他们面前树立形象的时候,你宣布你将在15分钟内做完这项任务。下面是解决的办法:

  添加一个新的编辑框到你的对话框资源中,并且添加相应的DDX变量。选择/clr编译模式并且添加下列代码到你的对话框的头文件中:

#include <msclr\auto_gcroot.h>
using namespace System::Security::Cryptography;

  使用auto_gcroot模板来声明一个MD5CryptoServiceProvider成员:

protected:
msclr::auto_gcroot<MD5CryptoServiceProvider^> md5;

  在OnInitDialog过程中,gcnew MD5CryptoServiceProvider成员。

md5 = gcnew MD5CryptoServiceProvider();

  并且为多行编辑框添加一个EN_CHANGE处理器:

void CXxxxxxDlg::OnEnChangeEdit1()
{
using namespace System;
CString str;
m_mesgedit.GetWindowText(str);
array<Byte>^ data = gcnew array<Byte>(str.GetLength());
for(int i=0; i<str.GetLength(); i++)
data[i] = static_cast<Byte>(str[i]);
array<Byte>^ hash = md5->ComputeHash(data);
CString strhash;
for each(Byte b in hash)
{
str.Format(_T("%2X "),b);
strhash += str;
}
m_md5edit.SetWindowText(strhash);
}

  这里使用了混合类型:一个本机Cdialog派生类,该类含有一个MD5CryptoServiceProvider成员(CLI类型)。你可以轻易地试验相反的情况(如早期的代码片断已显示的)——可以建立一个Windows表单应用程序而且可能想利用一个本机类库--这不成问题,使用上面定义的模板nativeroot即可。
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

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

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