一绪论 当微软推出VSNET实现了可扩展的托管C++后C++程序员们反映不一尽管大部分的程序员对于能够继续使用C++感到很欣慰但几乎所有的人对于托管C++提供的晦涩语法感到很痛苦微软明显从反馈中感觉到托管C++不是那么成功
年月日ECMA(欧洲计算机制造商协会)宣布成立专家组负责结合ISO标准C++与通用语言开发一个可扩展语言的标准这个新的可扩展语言被称为C++/CLI标准这个标准将被VSNET的C++编译器支持
二老语法存在的问题
晦涩繁琐的语法和文法这两个双重底线问题加重了阅读的负担
二流的CLI支持相对与C#与VBNETMC++使用不方便的工作区来提供CLI支持例如它没有一个一一对应的结构来列举NET的集合
C++与NET粗陋地结合对于CLI类型你不能使用C++的特色例如模板同样对于C++类型你不能使用CLI的特色例如碎片帐集
令人混淆的指针非托管的C++的指针及托管的引用指针都使用*语法这非常令人混淆因为gc指针与托管指针在本质和行为上完全不同
MFC编译器不能产生可校验的代码
三C++/CLI给我们提供了什么?
优雅流畅的语法和文法C++/CLI为C++开发人员书写托管代码提供了一种非常自然的感觉并且它提供了非托管代码到托管代码的平滑过度以前所谓的双重底线问题现在已经蕩然无存
一流的CLI支持CLI特色例如属性碎片集合和属类得到了直接支持此外C++/CLI还准许将这些特色用于本地非托管的类
一流的C++类支持C++特色例如模板和析构函数对于拖管和非拖管类继续有效实际上C++/CLI是你可以表面上在栈或C++本地堆上声明一个NET类型唯一的NET语言
在NET与C++之间的沟壑上架起了一座桥梁C++开发人员在抨击BCL时不再象离开水的鱼
C++/CLI编译器产生的可执行文件完全是可校验的
四Hello World小程序
using namespace System;void _tmain(){Console::WriteLine(Hello World);}
上述代码除了不需要引用mscorlibdll库外与老的语法没有太大的区别因为无论你什么时候使用/clr进行编辑编译器都可以暗中进行引用(现在默认的是/clr:newSyntax)
五句柄
与老的语法主要的混淆是我们习惯于使用*符号来声明拖管引用或非拖管指针在C++/CLI里微软引入了句柄的概念
void _tmain(){//The ^ punctuator represents a handleString^ str = Hello World;Console::WriteLine(str);}
^符号代表一个托管对象(声明时看上去象个帽子)按照CLI的规定句柄代表一个拖管对象的引用句柄在CLI中是新的语法相当于C++中的gc指针句柄与指针不再混淆在本质上两者完全不同
六句柄与指针是怎样区分开来的?
指针声明时使用*符号而句柄使用^符号
句柄是针对拖管堆上对象的拖管引用而指针仅仅指向内存中的一个地址
指针很稳定GC循环不会影响到它句柄在基于GC或内存紧张的情况下可以指向不同的内存位置
对于指针程序开发人员必须显式地删除否则会面临洩露的危险而对于句柄是否进行显式删除则完全根据程序人员的爱好了
句柄一定要指向一个具体的类型即所谓的类型安全性而指针明显不是这样你决不可以将一个句柄指向Void^类型
正如new操作符返回一个指针一样gcnew返回一个句柄
七CLR对象示例
void _tmain(){String^ str = gcnew String(Hello World);Object^ o = gcnew Object();Console::WriteLine(str);}
关键字gcnew用来实例化一个CLI对象而且它返回一个指向在CLR堆上的对象的句柄gcnew的优点在于它可以方便的让我们区分拖管和非拖管的实例对象
大部分情况下gcnew关键字和^操作符提供了你用来进行BCL的一切手段但是很明显你需要创建和声明属于自己的拖管类和接口
八声明类型
CLR类型有一个形容词前缀用来说明类型的种类下面是C++/CLI中的类型声明示例
CLR types o Reference types § ref class RefClass{}; § ref struct RefClass{}; Value types § value class ValClass{}; § value struct ValClass{}; o Interfaces § interface class IType{}; § interface struct IType{}; o Enumerations § enum class Color{}; § enum struct Color{}; Native types o class Native{}; o struct Native{}; 示例using namespace System;interface class IDog{void Bark();};ref class Dog : IDog{public:void Bark(){Console::WriteLine(Bow wow wow);}};void _tmain(){Dog^ d = gcnew Dog();d>Bark();}
上述程序中的代码与老的C++语言相比看上去非常简洁在以往的C++代码中至少要用到gc和interface这两个关键词
九装箱/拆箱操作
在C++/CLI中加箱是隐含的而且类型是安全的一个二进制的拷贝被执行并在CLR堆上形成一个对象去箱是显式的仅仅需要使用reinterpret_cast操作符来解除引用
void _tmain(){int z = ;Object^ o = z; //implicit boxingint y = *reinterpret_cast<int^>(o); //unboxingConsole::WriteLine({} {} {}ozy);z = ; Console::WriteLine({} {} {}ozy);}// 输出结果如下// //
在上述代码中o对象是一个加箱的拷贝从第二个语句Console::WriteLine的输出可以很明显地看到它并没有涉及到int类型的整数值
当你对一种数值类型进行加箱操作时返回的对象记住了最初的数值类型
void _tmain(){int z = ;float f = ;Object^ o = z; Object^ o = f; Console::WriteLine(o>GetType());Console::WriteLine(o>GetType()); }// Output// SystemInt// SystemSingle
因此不能对不同类型的对象进行去箱操作
void _tmain(){int z = ;float f = ;Object^ o = z; Object^ o = f;int y = *reinterpret_cast<int^>(o);//SystemInvalidCastExceptionfloat g = *reinterpret_cast<float^>(o);//SystemInvalidCastException}
如果你非尝试这么做那么你将得到一个SystemInvalidCastException让我们来探讨一下完美的类型安全性如果你要看内部代码你将看到微软的内部箱在实际中的运用例如
void Box(){float y=;Object^ o = y;}
编译后的代码是
maxstack locals (float V_ object V_)ldnullstlocldcr stlocldlocbox [mscorlib]SystemSinglestlocret
根据微软的内部文档箱操作将未加工的类型转换为一个具体类型的实例这项工作的完成通过创建一个新的对象并将数据拷贝到这个新分配的对象
十写在后面的话
为什么很多人已经可以使用CC++NET来开发程序但还在积极学习C++/CLI呢我想有四个方面的原因
从编译器直到内层都还在支持C++代码
C++/CLI对于其他标准来说无意是具有毁灭性地
与生俱来的内部支持胜过所有其他CLI语言
所有在MFC中出现的下划线都已不再存在