析構函數

析構函數 

析構函數(destructor) 與構造函數相反,當對象脫離其作用域時(例如對象所在的函數已調用完畢),系統自動執行析構函數。析構函數往往用來做“清理善後” 的工作(例如在建立對象時用new開闢了一片內存空間,應在退出前在析構函數中用delete釋放)。


以C++語言爲例,析構函數名也應與類名相同,只是在函數名前面加一個波浪符~,例如~stud( ),以區別於構造函數。如果用戶沒有編寫析構函數,編譯系統會自動生成一個缺省的析構函數,它也不進行任何操作。所以許多簡單的類中沒有用顯式的析構函數。

解構器

     我們知道,‘解構器’被用來清除類的事例。當我們在C#中使用解構器是,我們必須記住以下幾點:

       一個類只能有一個解構器。
       解構器不能被繼承或重載。
       解構器不能被調用。他們是自動被(編譯器)調用的。
       解構器不能帶修飾或參數。
   下面是類MyClass解構器的一個聲明:

 

~ Class()   
{  
    // Cleaning up code goes here  
}

 

     程序員不能控制解構器何時將被執行因爲這是由垃圾收集器決定的。垃圾收集器檢查不在被應用程序使用的對象。它認爲這些條件是符合清楚的並且收回它們的內存。解構器也在程序退出時被調用。當解構器執行時其背後所發生的那一幕是解構器隱式調用對象基類的Object.Finalize方法。因此上述解構器代碼被隱含轉化成:

 

protected override void Finalize()  
{  
    try  
    {  
       // Cleaning up .  
    }  
    finally  
    {  
       base.Finalize();  
    }
}

     現在,讓我們看一個解構器怎樣被調用的例子。我們有三個類A,B和C 。B派生自A,C派生自B。每個類有它們自己的構造器和解構。在類App的main函數中,我們創建C的對象。

 

using System;  
class A  
{  
public A()  
{  
   Console.WriteLine("Creating A");  
}  
~A()  
{  
   Console.WriteLine("Destroying A");  
}  
}  
   
class B:A  
{  
public B()  
{  
   Console.WriteLine("Creating B");  
}  
~B()  
{  
   Console.WriteLine("Destroying B");  
}  
   
}  
class C:B  
{  
public C()  
{  
   Console.WriteLine("Creating C");  
}  
   
~C()  
{  
   Console.WriteLine("Destroying C");  
}  
}  
class App  
{  
public static void Main()  
{  
   C c=new C();  
   Console.WriteLine("Object Created ");  
   Console.WriteLine("Press enter to Destroy it");  
   Console.ReadLine();  
   c=null;  
   //GC.Collect();  
   Console.Read();  
}  
}
  
     正如我們預料的,基類的構造器將會被執行並且程序會等待用戶按‘enter’。當這個發生,我們把類C的對象置爲null.但解構器沒有被執行..!!??正像我們所說的,程序員無法控制解構器何時被執行因爲這是由垃圾蒐集器決定的。但程序退出時解構器被調用了。你能通過重定向程序的o/p到文本文件來檢查這個。我將它輸出在這裏。注意到基類的解構器被調用了,因爲在背後base.Finalize()被調用了。

 

Creating A
Creating B
Creating C
Object Created
Press enter to Destroy it
Destroying C
Destroying B
Destroying A
 
  如果一旦你使用完對象你就想調用解構器,你該怎麼做?有兩個方法:

調用垃圾蒐集器來清理。

實現IDisposable的Dispose方法。

 

調用垃圾蒐集器

 

     你能通過調用GC.Collect方法強制垃圾蒐集器來清理內存,但在大多數情況下,這應該避免因爲它會導致性能問題。在上面的程序中,在GC.Collect()處移除註釋。編譯並運行它。現在,你能看到解構器在控制檯中被執行了。

 

實現IDisposable接口

 

     IDisposable 接口包括僅有的一個公共方法,其聲明爲void Dispose()。我們能實現這個方法來關閉或釋放非託管資源如實現了這個接口的類事例所控制的文件,流,和句柄等。這個方法被用做所有任務聯合對象的資源釋放。當實現了這個方法,對象必須尋求確保所有擁有的資源被繼承結構中關聯的資源也釋放(不能把握,翻不出來)。

 

class MyClass:IDisposable  
{  
     public void Dispose()  
{  
   //implementation  
}  
}

當我們實現了IDisposable接口時,我們需要規則來確保Dispose被適當地調用。

  

聯合使用解構器和IDisposable接口

 Public class MyClass:IDisposable  
{  
private bool IsDisposed=false;  
public void Dispose()  
{  
   Dispose(true);  
   GC.SupressFinalize(this);  
}  
protected void Dispose(bool Diposing) 

   if(!IsDisposed) 
   { 
       if(Disposing) 
      { 
          //Clean Up managed resources 
      } 
      //Clean up unmanaged resources 
   } 
   IsDisposed=true; 
}  
~MyClass()  
{  
   Dispose(false);  
}  
}

     在這裏重載了Dispose(bool)來做清理工作,並且所有的清理代碼都僅寫在這個方法中。這個方法被解構器和IDisposable.Dispose()兩着調用。我們應該注意Dispose(bool)沒有在任何地方被調用除了在IDisposable.Dispose()和解構器中。

     當一個客戶調用IDisposable.Dispose()時,客戶特意地想要清理託管的和非託管資源,並且因此完成清理工作。有一件你必須注意的事情是我們在清理資源之後立即調用了GC.SupressFinalize(this)。這個方法通知垃圾蒐集器不需要調用解構器,因爲我們已經做了清理。

     注意上面的例子,解構器使用參數false調用Dispose。這裏,我們確信垃圾蒐集器蒐集了託管資源。我們僅僅做非託管資源的清理。

 

結論

 

     儘管如此我們花費一些時間實現IDisposable接口,如果客戶不能合適地調用它們會怎樣?爲此C#有一個酷的解決方案。‘using’代碼塊。它看起來像這樣:

using (MyClass objCls =new MyClass())  
{  
   
}
 
     當控制從using塊通過成功運行到結束或者拋出異常退出時,MyClass的IDispose.Dispose()將會被執行。記住你例示的對象必須實現System.IDisposable接口。using語句定義了哪個對象將被清除的一個範圍。

 

轉載:http://www.cnblogs.com/paper/archive/2009/07/31/1535998.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章