技术开发 频道

C++/CLI:第一流的CLI语言(2)

【IT168 技术文档】

  栈语义和确定性的析构
 
  C++经由栈语义模仿给了我们确定性的析构。简言之,栈语义是Dispose模式的良好的语法替代品。但是它在语义上比C# using块语法更直观些。请看下列的C#和C++代码段(都做一样的事情-连接两个文件的内容并把它写到第三个文件中)。

  1) C#代码--使用块语义

public static void ConcatFilestoFile(String file1, String file2, String outfile) { String str; try{ using (StreamReader tr1 = new StreamReader(file1)) { using (StreamReader tr2 = new StreamReader(file2)) { using (StreamWriter sw = new StreamWriter(outfile)) { while ((str = tr1.ReadLine()) != null) sw.WriteLine(str); while ((str = tr2.ReadLine()) != null) sw.WriteLine(str); } } } } catch (Exception e) { Console.WriteLine(e.Message); } }
  2) C++代码--栈语义
static void ConcatFilestoFile(String^ file1, String^ file2, String^ outfile) { String^ str; try{ StreamReader tr1(file1); StreamReader tr2(file2); StreamWriter sw(outfile); while(str = tr1.ReadLine()) sw.WriteLine(str); while(str = tr2.ReadLine()) sw.WriteLine(str); } catch(Exception^ e) { Console::WriteLine(e->Message); } }
  C#代码与相等的C++ 代码相比不仅免不了冗长,而且using块语法让程序员自己明确地指定他想在哪儿调用Dispose(using块的结束处),而使用C++/CLI的栈语义,只需让编译器使用常规的范围规则来处理它即可。事实上,这使得在C#中修改代码比在C++中更乏味-作为一实例,让我们修改这些代码以便即使仅存在一个输入文件也能创建输出文件。请看下面修改后的C#和C++代码。
 
  3) 修改后的C#代码
public static void ConcatFilestoFile(String file1, String file2, String outfile) { String str; try{ using (StreamWriter sw = new StreamWriter(outfile)) { try{ using (StreamReader tr1 = new StreamReader(file1)) { while ((str = tr1.ReadLine()) != null) sw.WriteLine(str); } } catch (Exception) { } using (StreamReader tr2 = new StreamReader(file2)) { while ((str = tr2.ReadLine()) != null) sw.WriteLine(str); } } } catch (Exception e){ } }
  把针对StreamWriter的using块放到顶层需要重新调整using块结构--这在上面情况下显然不是个大问题,但是对于实际开发中的修改,这可能是相当模糊的且易导致逻辑错误的。
 
  4) 修改后的C++代码
static void ConcatFilestoFile(String^ file1, String^ file2, String^ outfile) { String^ str; try{ StreamWriter sw(outfile); try{ StreamReader tr1(file1); while(str = tr1.ReadLine()) sw.WriteLine(str); } catch(Exception^){} StreamReader tr2(file2); while(str = tr2.ReadLine()) sw.WriteLine(str); } catch(Exception^){} }

 

  这样不是比在C#中的做更容易些吗?我恰好把StreamWriter声明移到了顶部并增加了一个额外的try块,就这些。甚至对于象在我的示例代码片断中的琐碎事情,如果所涉及的复杂性在C++中大大减少,那么,当你工作于更大的工程时你能想象使用栈语义对你的编码效率千万的影响。
 
  还不确信?好,让我们看一下成员对象和它们的析构吧。Imagine CLI GC类R1和R2,二者都实现了Idisposable接口且都有函数F(),还有一个CLI GC类R,它有R1和R2成员和一个函数F()-它内部地调用R1和R2上的F()成员函数。让我们先看C#实现。

  5) 一个disposable类继承层次的C#实现

class R1 : IDisposable{ public void Dispose() { } public void F() { } } class R2 : IDisposable{ public void Dispose() { } public void F() { } } class R : IDisposable{ R1 m_r1 = new R1(); R2 m_r2 = new R2(); public void Dispose() { m_r1.Dispose(); m_r2.Dispose(); } public void F() { m_r1.F(); m_r2.F(); } public static void CallR() { using(R r = new R()) {r.F();} } }
  这里有几件事情要做:必须为每个disposable 类手工实现IDisposable接口,对于具有成员R1和R2的类R,Dispose方法也需要调用成员类上的Dispose。现在让我们分析上面几个类的C++实现。
 
  6) 等价的C++实现
ref class R1 { public: ~R1(){} void F(){} }; ref class R2 { public: ~R2(){} void F(){} }; ref class R { R1 m_r1; R2 m_r2; public: ~R(){} void F() { m_r1.F(); m_r2.F(); } static void CallR() { R r; r.F(); } };
  注意,这里不再有手工的Idisposable接口实现(我们的类中仅建立了析构器)而且最好的部分--类R的析构器(Dispose方法)并没有在该类可能含有的可释放的成员上调用Dispose-它没有必要这样做,编译器自动为之生成所有的代码!

 

0
相关文章