技术开发 频道

体验Visual C++ 2005的现代语言特性(1)

  在此说明一下是如何定义一个实例的,托管引用类建立于托管堆中,本地类型建立于堆栈或本地堆中,当一个托管引用类被声明建立在堆栈中时,编译器实际上还是在托管堆中把它实例化,如下图:


(在托管堆中实例化)
 
  上图中所示的托管引用类型也带来了一些问题:当一个堆栈中的实例对象超出了范围,将会有什么问题发生呢?它是怎样被清理的呢?许多C#的开发者抱怨C#语言缺乏确定性清理,C#语言可通过使用关键字,非常容易地清除IDisposable的对象,但这需要额外的代码,并且与C++开发者熟悉的析构模式相比,显得有点晦涩。在C#中,安全清理在默认状态下是关闭的,要打开它,还需要显式编码。举例来说,看一下插3中的C#代码片断,对象StreamReader声明在托管堆中,当类中的方法执行完毕后,对StreamReader的实例就不再有任何引用了,但是这个对象仍然不会结束,直到垃圾回收器回收它;而且,文件不会被关闭,程序依然拥有打开文件的句柄。要加入确定性清理,必须通过那些使用非托管资源的类来实现IDisposable接口。
 
  代码2中的第二个示例,演示了C#中新的代码,此处的代码仍具有不错的可读性,但一旦引入更多需要清理的对象时,代码就会变得越来越难读懂了,同样,当垃圾回收器最终运行时,任何忘记清除的对象,都会加重最终结束器线程(finalizer)的负担,同时,还有可能锁住高价值资源。而且,当用Visual Basic .NET来实现同样的功能时,这种编码形式会变得更加丑陋(虽然Visual Basic 2005也有一个类似于C#的Using声明)。
 
  代码2:确定性清理代码

  实现代码

  C#(没有确定性清理)

string ReadFirstLineFromFile(string path) { StreamReader reader = new StreamReader(path); return reader.ReadLine(); }
  C#(有确定性清理)
string ReadFirstLineFromFile(string path) {  using (StreamReader reader = new StreamReader(path))  {   return reader.ReadLine();  } }
  Visual Basic .NET(有确定性清理)
Function ReadFirstLineFromFile( _ByVal path As String) As String Dim reader As StreamReader Try  reader = New StreamReader(path)  ReadFirstLineFromFile = reader.ReadLine() Finally  If Not reader Is Nothing Then _   CType(reader, IDisposable).Dispose() End Try End Function
  C++(有确定性清理)
String^ ReadFirstLineFromFile(String^ path) {  StreamReader reader(path);  return reader.ReadLine(); }
  现在,Visual C++ 2005对任何类型的托管和本地对象,都提供了一个析构函数或结束器(finalizer)。当类型是托管时,编译器映射一个析构函数到IDisposable::Dispose方法中,这意味着,你可用C++来编写同样的方法——如插3中的第四段代码,而且reader的析构或清除方法会被自动调用,就好像在C#中使用using一样。这样,当创建在堆栈上的类型超出作用范围之后,它的析构函数就会被调用。
 
  托管扩展带来的最大问题就是指针问题了,虽然指针很难理解,但却是应付多种任务和多种情况的多面手。在Visual C++ 2005中,指针仍旧是老式的C++指针,它指向一个对象,并能执行一些算法。引用一个对象的指针,它的生存期必须由开发者显示地管理,当与指针打交道时,运行时库可不负责清理它。
 
  现在,来看一下Visual C++ 2005的设计者是怎样实现的吧,Visual Studio .NET 2003和Visual Studio .NET 2005中的new操作符通常返回一个指针,而gcnew操作符返回一个句柄,一个用脱字符 ^ 语法表示的一个结构,此句柄指向托管堆的对象。因此,它们不能指向interior类型,而且在用法上,编译器也作了不少限制,以便开发者正确、安全地使用它们。句柄不能执行指针算法,也不能转换成一个空指针或任何其他整数类型,话说回来,依然可使用星号(*)和箭号(->)操作符。
 
  这不是说,你再不能取得一个指向垃圾回收堆中的指针了,在Visual C++ 2005中的pin_ptr,可用于取得托管堆中对象的一个固定指针,只要这个指针存在,对象就被固定在托管堆中,以防止垃圾回收器清除它;Visual C++ 2005同时也引入了引用跟踪操作符,以百分号 % 表示。当年在C++中引入 & 引用操作符时,大多数开发者把它理解成一个指向对象的指针,而且由编译器自动解引用。在很多方面来说,% 之于 ^,就像 & 之于 *。
 
  在托管世界中,对托管对象的本地引用,与指向托管对象的本地指针一样危险,指针和引用的基本原理在于,被引用的对象不能四处移动。引用跟踪与本地引用非常类似,除了它引用的对象是在托管堆中,而且被垃圾回收器移动之后,还能继续跟踪它们。百分号 % 操作符用来取托管对象的地址,就像 & 操作符对于本地对象的功能一样,百分号 % 操作符可返回一个托管引用类型对象的句柄。
 
  一般来说,C++开发者知道ISO标准控制着语言的发展方向,正是因为这个原因,为了提高被第三方采用的机率,同时保证语言稳步向前发展,这种新语法被提议为一种称作C++/CLI的标准。在2003年10月,ECMA(欧洲计算机制造商协会)投票决定建立一个特别工作组——TG5,专门负责分析和采用这个标准,其工作性质就像WG21是ISO C++的管理单位一样,实际上,WG21中的关键组员也同时服务于TG5中。计划在2004年底,把C++/CLI标准化。

 

 

 

 

0
相关文章