对于谙于标准C++的程序员来说,最为关心的还是:在C++/CLI中,世界还是他们熟悉的那个世界吗?在标准C++的世界里,他们手里的各种魔棒——操作符重载|模板|多继承(语言),STL|Boost|ACE(库)——还能挥舞出五彩缤纷的火焰吗?是不是标准C++到了.NET环境下就像被拔掉了牙的老虎一样——Managed C++ Extension的阴影是不是还笼罩在他们的心头?
答案是:以前你所能做的,现在仍然能做,世界只是变得更广阔了——
什么是C++/CLI?
l C++/CLI是一集标准化的语言扩展(对标准C++进行扩展),而并非另起炉灶的另一门新语言。所以C++/CLI是标准C++的一个超集。
l C++/CLI是一门ECMA标准[1](并且会被提交给ISO标准化委员会),而不是微软的专有语言。参与C++/CLI标准的修订的有很多组织(或公司),其中包括Edison Design Group,Dinkumware公司等,微软在把C++/CLI标准草案提交给ECMA组织后就放弃了对其的控制权,而是将它作为一份公开发展的标准,任何使用C++/CLI的用户都可以为它的发展提出自己的建议。
l C++/CLI的目的是把C++带到CLI平台上,使C++能够在CLI平台上发挥最大的能力。而并非把C++约束在CLI平台(CLI本身也是ISO标准化的)上。相反,原来标准C++的能力丝毫没有减弱,并且,通过C++/CLI中的标准扩展,C++具有了原来没有的动态编程能力以及一系列的first class的.NET特性。这些扩展并非是专有的,而是以一种标准的方式呈现。
C++/CLI有什么优越性?
l 动态编程和refelection——标准C++是一门非常静态的语言,其原则是尽量在编译期对程序的合法性和逻辑作出检查。而在运行时的动态信息方面,标准C++是有所欠缺的。例如,标准C++在运行期能够对动态对象进行查询的就只有typeid操作符,而typeid()返回的typeinfo类虽然是个唯一标识,但是也仅仅止于“唯一”而已,首先标准C++并未规定typeinfo的底层二进制表示,所以用它作为跨平台的类唯一标识符就不可能了,其次typeinfo类几乎仅仅就表示类名字而已,这种非常“薄”的运行时类型信息阻止了标准C++在分布式领域的能力(IDL就是为了弥补标准C++在运行期类型信息的不足,但是IDL对于拥有元数据的语言如JAVA或C#根本就不是必须的,同时IDL也使C++在分布式领域的使用不那么简易)。由于标准C++的Native特点,所以其代码一经编译便几乎丧失所有的类型信息,从而使得运行期无法对程序本身做几乎任何的改动,换句话说,标准C++的代码一经编译就几乎变成了死的。而C++/CLI改变了这一现状,C++/CLI拥有完备的元数据,允许程序在运行期查询完整的类型信息,并可以由类型信息动态创建对象,甚至可以动态创建类型,添加方法等等,这种强大的运行期的动态特性对于现代应用领域(例如分布式WEB应用)是必须的。
l GC——现在谁也不会说“往C++中加入GC就是终结了C++”这种话了。就连Bjarne Stroustrup也同意如果C++要被用于大型或超大型的软件开发中去,最好要有一个良好的可选的GC支持。GC与否,已经不再是个值得争论的问题,问题是,我们如何把它实现的更好——C++/CLI中的GC是目前最为强大的GC机制之一——分代垃圾收集。GC的好处是可以简化软件的开发模型,在效率并非极其关键的领域,GC可以很大程度上提高生产率。C++/CLI中的GC很重要的一点是:它正是可选的。这一点不同于JAVA或C#,对于后者,GC无处不在,对象只能分配在托管堆上。而在C++/CLI中,如果把你的对象分配在Native Heap上,你就得到和标准C++一样的高效内存管理。如果把对象分配在Managed Heap上,那么该对象的内存就由GC来自动回收。这种混合式的内存管理环境和C++/CLI的定位有关——毕竟,C++/CLI的定位是.NET平台上的系统级编程语言,所以效率以及对底层的控制很重要,故保留了Native Heap。后面你会看到这种编程环境的优点。
l BCL——.NET平台上的基础类库,BCL中丰富的类极大的方便了开发者。C++/CLI可以完全使用BCL中的任何类。
l 可移植性——毫无疑问,可移植性是个至关重要的问题,特别是对于标准C++社群的人们。C++/CLI对这个问题的答案是:“如果你的代码不依赖于本地二进制库,就可以“一次编译,随处运行(在.NET平台上)”(使用“/clr:pure”编译选项将代码编译成可移植的纯MSIL代码)。如果你的代码某部分依赖于本地的二进制库(如C输入输出流库),那么这些部分仍然是源代码可移植的,而其它部分则可以“一次编译,随处运行”。对于标准C++来说,向来保证的只是源代码的可移植性,所以我们并没有失去什么,相反,如果遵守协定——不用本地二进制库,例如,用BCL里的输入输出流库代替C输入输出流库——你就可以得到“一次编译,随处运行”的承诺,也就是说,你的代码经过编译(/clr:pure)后可以在其它任何.NET平台上运行——Unix,Linux下的Mono(移植到Unix,Linux下的.NET),以及FreeBSD,Mac OSX下的Rotor(.NET的开放源代码项目),等等。
l 效率——作为.NET平台上的系统级编程语言,C++/CLI混合了Native和Managed两种环境。而不象C#那样只能进行托管编程。所以相对来说,C++/CLI可以具有更高的效率——前提是你愿意把效率敏感的代码用极具效率的Native C++来写(当然,谁不愿意呢?)另外,因为标准C++是静态语言,所以作为标准C++的一个超集的C++/CLI能够在编译期得到更多的优化(静态语言总是能够得到更多的优化,因为编译器能够知道更多的信息),从而具有更高的效率。相比之下,C#的编译期优化就弱了很多。
l 混合式的编程环境——这是C++/CLI独有的一种编程环境。你既可以进行高效的底层开发——对应于C++/CLI的标准C++子集,也可以在效率要求不那么严格的地方使用托管式编程以提高生产率。然后把两者平滑的联结在一起,而这一切都在你熟悉的编程语言中完成,你使用你熟悉的编程习惯,熟悉的库,熟悉的语言特性和风格… 不需要从头学习一门新的语言,不需要面对语言之间如何交互的问题。
l 习惯——谁也不能小觑习惯的力量。对于标准C++程序员,如果要在.NET平台上开发,C++/CLI是毫无疑问的首选语言,因为他们在标准C++中积累起来的任何编程技巧,惯用法,以及对库的使用经验,代码的表达方式等等全都可以“移植”到C++/CLI中。C++/CLI保持对标准C++代码的完全兼容,同时以最小最一致的语法扩展提供托管环境下编程的必要语义。
还记得在Managed C++ Extension世界里是如何访问托管类的吗?丑陋的__gc关键字无处不在——事实上,不仅是“丑陋”而已(MC++为什么会消亡?)。而在C++/CLI里则引入了一个新的语法元素,名为Handle,写作“^”——你可以把它看成Managed世界里的Pointer(不过不能进行指针算术)。