托管C++也与此类似,看起来像析构函数的代码其实是一个Finalize方法,编译器实际上插入了一个try-finally块并调用基类的Finalize方法,因此,C#与托管C++相对容易编写一个Finalize方法,但在编写Dispose方法时,却没有提供任何帮助。程序员们经常使用Dispose方法,把它当作一个伪析构函数以便在代码块末执行一点其他的代码,而不是为了释放任何资源。
C++/CLI认识到了Dispose方法的重要性,并在引用类型中,使之成为一个逻辑"析构函数"。
ref class Derived : Base { ~Derived() //实现或重载IDisposable::Dispose方法 { //释放托管与非托管资源 }
!Derived() //实现或重载IDisposable::Dispose方法 { //只释放非托管资源 } }; |
对C++程序员来说,这让人感觉更自然了,能像以往那样,在析构函数中释放资源了。编译器会生成必要的IL(中间语言)来正确实现IDisposable::Dispose方法,包括抑制垃圾回收器调用对象的任何Finalize方法。事实上,在C++/CLI中,显式地实现Dispose方法是不合法的,而从IDisposable继承只会导致一个编译错误。当然,一旦类型通过编译,所有使用该类型的CLI语言,将只会看到Dispose模式以其每种语言最自然的方式得以实现。在C#中,可以直接调用Dispose方法,或使用一个using语句--如果类型定义在C#中。那么C++呢?难道要对堆中的对象正常地调用析构函数?此处当然是使用delete操作符了,对一个句柄使用delete操作符将会调用此对象的Dispose方法,而回收对象的内存是垃圾回收器该做的事,我们不需要关心释放那部分内存,只要释放对象的资源就行了。
Derived^ d = gcnew Derived(); d->SomeMethod() delete d; |
如果表达式中传递给delete操作符的是一个句柄,将会调用对象的Dispose方法,如果此时再没有其他对象链接到引用类型,垃圾回收器就会释放对象所占用的内存。如果表达式中是一个本地C++对象,在释放内存之前,还会调用对象的析构函数。
毫无疑问,在对象生命期管理上,我们越来越接近自然的C++语法,但要时刻记住使用delete操作符,却不是件易事。C++/CLI允许对引用类型使用堆栈语义,这意味着你能用在堆栈上分配对象的语法来使用一个引用类型,编译器会提供给你所期望的C++语义,而在底层,实际上仍是在托管堆中分配对象,以满足CLR的需要。
Derived d; d.SomeMethod(); |
当d超出范围时,它的Dispose将会被调用,以释放它所占用的资源。再则,因为对象实际是在托管堆中分配的,所以垃圾回收器会在它的生命期结束时释放它。来看一个ADO.NET的例子,它与C++/CLI中的概念非常相似。
SqlConnection connection("Database=master; Integrated Security=sspi");
SqlCommand^ command = connection.CreateCommand(); command->CommandText = "sp_databases"; command->CommandType = CommandType::StoredProcedure;
connection.Open();
SqlDataReader reader(command->ExecuteReader());
while (reader.Read()) { Console::WriteLine(reader.GetString(0)); } |
查看本文来源