摘 要:建構函式與析構函式是乙個類中看似較為簡單的兩類函式,但在實際運用過程中總會出現一些意想不到的執行錯誤。本文將較系統的介紹建構函式與析構函式的原理及在c#中的運用,以及在使用過程中需要注意的若干事項。
關鍵字:建構函式;析構函式;垃圾**器;非託管資源;託管資源
一.建構函式與析構函式的原理
作為比c更先進的語言,c#提供了更好的機制來增強程式的安全性。c#編譯器具有嚴格的型別安全檢查功能,它幾乎能找出程式中所有的語法問題,這的確幫了程式設計師的大忙。但是程式通過了編譯檢查並不表示錯誤已經不存在了,在「錯誤」的大家庭裡,「語法錯誤」的地位只能算是冰山一角。級別高的錯誤通常隱藏得很深,不容易發現。
根據經驗,不少難以察覺的程式錯誤是由於變數沒有被正確初始化或清除造成的,而初始化和清除工作很容易被人遺忘。微軟利用物件導向的概念在設計c#語言時充分考慮了這個問題並很好地予以解決:把物件的初始化工作放在建構函式中,把清除工作放在析構函式中。當物件被建立時,建構函式被自動執行。當物件消亡時,析構函式被自動執行。這樣就不用擔心忘記物件的初始化和清除工作。
二.建構函式在c#中的運用
建構函式的名字不能隨便起,必須讓編譯器認得出才可以被自動執行。它的命名方法既簡單又合理:讓建構函式與類同名。除了名字外,建構函式的另乙個特別之處是沒有返回值型別,這與返回值型別為void的函式不同。如果它有返回值型別,那麼編譯器將不知所措。在你可以訪問乙個類的方法、屬性或任何其它東西之前, 第一條執行的語句是包含有相應類的建構函式。甚至你自己不寫乙個建構函式,也會有乙個預設建構函式提供給你。
class testclass
// 由clr提供
} 下面列舉了幾種型別的建構函式
1)預設建構函式
class testclass }
上面已介紹,它由系統(clr)提供。
2)例項建構函式
例項建構函式是實現對類中例項進行初始化的方法成員。如:
using system;
class point
public point(double x, double y)…}
class test
} 宣告了乙個類point,它提供了兩個建構函式。它們是過載的。乙個是沒有引數的point建構函式和乙個是有兩個double引數的point建構函式。如果類中沒有提供這些建構函式,那麼會clr會自動提供乙個預設建構函式的。但一旦類中提供了自定義的建構函式,如point()和point(double x, double y),則預設建構函式將不會被提供,這一點要注意。
3) 靜態建構函式
靜態建構函式是實現對乙個類進行初始化的方法成員。它一般用於對靜態資料的初始化。靜態建構函式不能有引數,不能有修飾符而且不能被呼叫,當類被載入時,類的靜態建構函式自動被呼叫。如:
using system.data;
class employee
...}
宣告了乙個有靜態建構函式的類employee。注意靜態建構函式只能對靜態資料成員進行初始化,而不能對非靜態資料成員進行初始化。但是,非靜態建構函式既可以對靜態資料成員賦值,也可以對非靜態資料成員進行初始化。
如果類僅包含靜態成員,你可以建立乙個private的建構函式:private testclass() ,但是private意味著從類的外面不可能訪問該建構函式。所以,它不能被呼叫,且沒有物件可以被該類定義例項化。
以上是幾種型別建構函式的簡單運用,下面將重點介紹一下在類的層次結構中(即繼承結構中)基類和派生類的建構函式的使用方式。派生類物件的初始化由基類和派生類共同完成:基類的成員由基類的建構函式初始化,派生類的成員由派生類的建構函式初始化。
當建立派生類的物件時,系統將會呼叫基類的建構函式和派生類的建構函式,構 造函式的執行次序是:先執行基類的建構函式,再執行派生類的建構函式。如果派生類又有物件成員,則,先執行基類的建構函式,再執行成員物件類的建構函式,最後執行派生類的建構函式。
至於執行基類的什麼建構函式,預設情況下是執行基類的無參建構函式,如果要執行基類的有參建構函式,則必須在派生類建構函式的成員初始化表中指出。如:
class a
public a( int i )
};class b : a
public b( int i )
public b( int i, int j ):a(i)
};b b1 = new b(); //執行基類a的建構函式a(),再執行派生類的建構函式b()
b b2 = new b(1); //執行基類a的建構函式a(),再執行派生類的建構函式b(int)
b b3 = new b(0,1); //執行執行基類a的建構函式a(int) ,再執行派生類的
建構函式b(int,int)
在這裡建構函式的執行次序是一定要分析清楚的。另外,如果基類a中沒有提供無參建構函式public a( ) ,則在派生類的所有建構函式成員初始化表中必須指出基類a的有參建構函式a(i),如下所示:
class a
};class b : a
public b(int i):a(i)
public b(int i, int j):a(i)
}; 三.析構函式和垃圾**器在c#中的運用
析構函式是實現銷毀乙個類的例項的方法成員。析構函式不能有引數,不能任何修飾符而且不能被呼叫。由於析構函式的目的與建構函式的相反,就加字首『~』以示區別。
雖然c#(更確切的說是clr)提供了一種新的記憶體管理機制---自動記憶體管理機制(automatic memory management),資源的釋放是可以通過「垃圾**器」 自動完成的,一般不需要使用者干預,但在有些特殊情況下還是需要用到析構函式的,如在c#中非託管資源的釋放。
資源的釋放一般是通過"垃圾**器"自動完成的,但具體來說,仍有些需要注意的地方:
1. 值型別和引用型別的引用其實是不需要什麼"垃圾**器"來釋放記憶體的,因為當它們出了作用域後會自動釋放所佔記憶體,因為它們都儲存在棧(stack)中;
2. 只有引用型別的引用所指向的物件例項才儲存在堆(heap)中,而堆因為是乙個自由儲存空間,所以它並沒有像"棧"那樣有生存期("棧"的元素彈出後就代表生存期結束,也就代表釋放了記憶體),並且要注意的是,"垃圾**器"只對這塊區域起作用;
然而,有些情況下,當需要釋放非託管資源時,就必須通過寫**的方式來解決。通常是使用析構函式釋放非託管資源,將使用者自己編寫的釋放非託管資源的**段放在析構函式中即可。需要注意的是,如果乙個類中沒有使用到非託管資源,那麼一定不要定義析構函式,這是因為物件執行了析構函式,那麼"垃圾**器"在釋放託管資源之前要先呼叫析構函式,然後第二次才真正釋放託管資源,這樣一來,兩次刪除動作的花銷比一次大多的。下面使用一段**來示析構函式是如何使用的:
public class resourceholder
四.小結
建構函式與析構函式雖然是乙個類中形式上較簡單的函式,但它們的使用決非看上去那麼簡單,因此靈活而正確的使用建構函式與析構函式能夠幫你更好的理解clr的記憶體管理機制,以及更好的管理系統中的資源。
C 建構函式 析構函式 虛析構函式
一般地,建立物件和刪除物件時,父類建構函式 子類建構函式 子類析構函式 父類析構函式。特例 如果用new建立了乙個物件,並將父類的指標指向這個子類的物件,那麼用delete撤銷物件時,系統只執行基類的析構函式,而不執行派生類的析構函式。如果希望按照子類析構函式 父類析構函式的順序執行,那麼應該將基類...
C 建構函式,析構函式
一 建構函式 在建立類的物件時自動呼叫的乙個函式,主要是為物件本身做初始化工作 沒有返回值,名字和類的名字相同,因為是在建立物件時候自動呼叫的,所以必須型別為public的,可以帶引數,可以有多個 如果類中沒有建構函式,在下面三種情況下,會自動的新增預設的建構函式 1.如果類中有虛函式,或者類中有虛...
C 建構函式 析構函式
在學習類的成員方法時,遇到了兩個函式。簡單介紹一下 一 建構函式 建構函式是類中比較特殊的成員函式,它也是一種方法。主要用來為物件分配儲存空間,完成初始化操作,例如給類的成員變數賦值等。一般來說,建構函式一旦開始執行,那麼物件的生命週期就開始了,最終它將以析構函式結束。如果乙個類含有建構函式,在例項...