用Visual C++ 2005编写更快的代码
对于c++语言的爱好者来说,visual studio .net 2003中c++编译器的引入绝对令人垂涎欲滴。visual c++ .net 2003中有98%的部分与iso c++标准保持一致,这使它比以往任何版本更为靠近这些标准,而且它还加入了对一些功能(如局部模板专用化)的语言支持。它还包括增强的缓冲区安全检查和改进的编译器诊断功能。c++开发人员就像c#和visual basic .net开发人员一样,可以使用拖放窗体设计器来构建健壮的windows窗体应用程序。该编译器还包含了针对intel pentium 4和amd athlon处理器的优化。
如果您对visual c++ .net 2003感到兴奋不已,您将会更加疯狂地爱上它的下一个版本visual c++ 2005.visual c++ 2005为。net开发提供了既优雅又强大的新语法支持。全新的优化技术已经使microsoft产品的运行速度提高了30%.它通过新的编译模式来确保microsoft .net framework通用语言基础结构(common language infrastructure,cli)的一致性和可验证性,并且具有新的互操作(interop)模型,这不仅提供了本机和托管环境的无缝融合,而且还在跨边界的情况下提供了完全控制。该编译器增强了前两个版本中提供的缓冲区安全检查选项,并且还包括了c++应用程序普遍使用的以安全性为中心的库的新版本。它提供了对openmp标准以及64位平台(其中包括intel itanium和amd64芯片)的支持。它解决了混合dll加载问题,并且提供了对double p/invoke性能问题的运行时的自动清除。还可以列出许多增强和改进。正如c++小组的一位架构师告诉我的,“兄弟,c++总算找到了属于自己的位置!”
c++/cli新的语法
在我们这些人中,有多少人讨厌使用前两个版本c++的托管扩展语法并且认为其中尽是错误?有多少人认为visual c++没有被当作基于。net的头号语言?很明显,当中的大多数人都是这样的(其中包括开发团队本身,只要阅读一下他们的blog就知道了)。visual c++小组的人听到了这些抱怨,于是开始开发visual c++.与visual studio .net 2002一起引入的c++语法的托管扩展就像恐龙一样消失殆尽了,因为引入了修订的语言定义,从而产生了一种有吸引力的新语法。
设计小组对于这个版本在语言设计方面有几个重要的目标。首先(可能对那些认为代码是一种艺术的人来说最为重要),他们想要确保编程人员在编写c++代码时感到很自然,而且通过对iso c++标准的纯粹扩展可以提供一种优雅的语法。他们想要让编程人员轻松地使用c++编写可验证的代码来支持部分信任的情况,例如sql server 2005中的clickonce部署、窗体设计支持和托管代码宿主。他们不想为任何比c++“更低级”的语言提供任何空间。他们想把。net的全部强大功能带给c++,而与此同时也把c++的强大功能带给。net.他们在各个方面都取得了骄人的成功。
新的扩展规范叫做c++/cli,并且现在正在进行标准化的工作。要体验一下新的语言扩展,请参见于2003年9月21日在线公布的候选基本文档。
对任何阅读采用新语法编写的代码的人来说,最容易注意到曾经在托管扩展中以双下划线关键字定义垃圾回收类、属性等的流行做法已经成为过去。虽然这样一些关键字仍然保留着,并且还加入了一些新的关键字,但它们现在已经不经常使用了,并且也不会影响到代码的可读性。这些双下划线的关键字由两种新类型的关键字来代替:上下文敏感的关键字和间隔排列的关键字。上下文敏感的关键字是只有在特定上下文中才使用的关键字,而间隔排列的关键字是在与其他关键字组合时才使用的关键字。例如,托管扩展中的__property关键字会被property关键字取代(不仅如此,用来定义一个属性及其访问器的全部语法都有了显著的改进,使得声明看起来非常类似于用c#编写的代码。请参见图1中的示例)。这并不影响在编码时将“property”用作变量的名称。在声明某一类型的属性这一上下文中,被解析为“property”的标记仅被视为一个关键字。[来源www.iocblog.net]
图 1 语法对比
托管扩展语法
public __gc __sealed class student
{
private:
double m_grade;
string* m_name;
public:
__property double get_grade() { return m_grade; }
__property void set_grade(double newgrade) { m_grade = newgrade; }
__property string* get_name() { return m_name; }
__property void set_name(string* newname) { m_name = newname; }
}
c++/cli 语法
public ref class student sealed
{
private:
double m_grade;
public:
// standard property syntax
property double grade
{
double get() { return m_grade; }
void set(double newgrade) { m_grade = newgrade; }
}
// trivial property
// compiler can generate accessors and backing store
property string^ name;
}
在新的语法中,类型以“形容词类”的形式声明,其中,形容词描述您正在创建的类是什么类型,如下所示:[来源www.iocblog.net]
class n { /*…*/ }; // native type
ref class r { /*…*/ }; // clr reference type
value class v { /*…*/ }; // clr value type
interface class i { /*…*/ }; // clr interface type
enum class e { /*…*/ }; // clr enumeration type
在之前的语言版本中,类型被声明时就可以确定它的使用范围及方式。只有本机类或结构和托管值类型可以在堆栈上创建。托管引用类总是存在于托管堆当中。在visual c++ 2005中,所有的类型,无论是本机的还是托管的,都在堆栈上创建,它使用基于堆栈的确定性清理语义来完成这一功能。
要在本机堆上实例化类型t的一个对象,可以使用“new t”。这样就可以返回一个指向本机堆上的对象地址的指针(一个在visual studio .net 2002和visual studio .net 2003中称为__nogc指针概念)。为了在托管堆上实例化类型t的一个对象,visual c++ 2005引入了gcnew这一关键字,它与new关键字的使用方式相同。调用“gcnew t”可以返回指向托管堆中整个对象的一个句柄。句柄(handler)是在visual c++ 2005中引入的一个新构造,它类似于托管扩展中的__gc指针。要在堆栈上实例化t类型的对象,标准的“t t;”声明就已经足够了。
为了公平起见,还是介绍一下是如何定义实例化的。托管引用类总是存在于托管堆当中,而本机类型总是存在于堆栈或本机堆当中。当一个托管引用被声明为存在于堆栈上时,编译器实际上还会在托管堆上对其进行实例化。
这样会带来一些问题。当在堆栈上的实例超出它的使用范围时会怎样?这个实例将如何被清理掉?许多c#开发人员一直在抱怨c#语言缺少确定性清理。c#语言提供using关键字来简化idisposable对象的处置,但这需要额外的代码,而且与c++开发人员所熟悉的析构函数的模式相比显得尤为笨拙。在c#中,安全的清理工作在默认情况下是无法进行的,它需要进行显式的编码。例如,请考虑图3中的第一个c#代码片断。streamreader对象是在托管堆上声明的。当这个方法执行完毕之后,streamreader的实例就没有任何引用存在了。然而,直到垃圾回收器运行时,这个对象才会被清理掉。直到那时,所用的文件才会被关闭,而在此之前,应用程序会一直占用其打开的文件句柄。要添加确定性清除,必须使用由利用非托管资源的类实现的idisposable接口。
图3中的第二个代码示例显示了c#中的新代码的外观。其实这种方法也未尝不可,而且也还算有一定的可读性。但当开始加入更多需要清理的对象时,您的代码就会变得越来越难懂。而且,任何您忘记清理的对象都会在最后垃圾回收器实际运行时为finalizer线程增加负担。而与此同时,也许已经锁定了一些有价值的资源。这一点在查看visual basic .net中的同等实现时显得尤为不堪,同样如图3所示(尽管visual basic 2005增加了与c#相类似的using语句)。