建構函式與解構函式是一個類中看似較為簡單的兩類函數,但在實際運用過程中總會出現一些意想不到的運行錯誤。本文將較系統的介紹建構函式與解構函式的原理
及在C#中的運用,以及在使用過程中需要注意的若干事項。
C#建構函式
建構函式是一種特殊的成員函數,它主要用於為對象分配儲存空間,對資料成員進行初始化.
(1)建構函式的名字必須與類同名;(2)建構函式沒有傳回型別,它可以帶參數,也可以不帶參數;(3)建構函式的主要作用是完成對類的初始化工作;(4)在建立一個類的新對象(使用new關鍵字)時,系統回自動調用給類的建構函式初始化新對象;
C#的類有兩種建構函式:執行個體建構函式和靜態建構函式
執行個體建構函式:負責初始化類中的執行個體變數,它只有在使用者用new關鍵字為對象分配記憶體時才被調用,而且作為參考型別的類,其執行個體化後的對象必然時分配在托
管堆(Managed
Heap)上。執行個體建構函式又分為預設建構函式和非預設建構函式,注意一旦類有了自己的建構函式,無論是有參數還是沒有參數,預設建構函式都將無效,而且
僅僅聲名一個類而不執行個體化它,則不會調用建構函式。 靜態建構函式: (1)用於對靜態欄位、唯讀欄位等的初始化;
(2)添加static關鍵字,不能添加存取修飾詞,因為靜態建構函式都是私人的;
(3)類的靜態建構函式在給定應用程式定義域中至多執行一次,只有建立類的執行個體或者引用類的任何靜態成員才激發,不能帶又參數;
(4)靜態建構函式是不可繼承的,而且不能被直接調用;
(5)如果類中包含用來開始執行的 Main 方法,則該類的靜態建構函式將在調用 Main 方法之前執行.任何帶有初始值設定項的靜態欄位,則在執行該類的靜態建構函式時,先要按照文本順序執行那些初始值設定項;
(6)如果沒有編寫靜態建構函式,而這時類中包含帶有初始值設定的靜態欄位,那麼編譯器會自動產生預設的靜態建構函式; 一個類可以同時擁有執行個體建構函式和靜態建構函式,這是惟一可以具有相同參數列表的同名方法共存的情況。
轉自:http://tb.blog.csdn.net/TrackBack.aspx?PostId=1622612
一.建構函式與解構函式的原理
作為比C更先進的語言,C#提供了更好的機制來增強程式的安全性。C#編譯器具有嚴格的
型別安全檢查功能,它幾乎能找出程式中所有的文法問題,這的確幫了程式員的大忙。但是程式通過了編譯檢查並不表示錯誤已經不存在了,在“錯誤”的大家庭
裡,“語法錯誤”的地位只能算是冰山一角。層級高的錯誤通常隱藏得很深,不容易發現。
根據經驗,不少難以察覺的程式錯誤是由於變數沒
有被正確初始化或清除造成的,而初始化和清除工作很容易被人遺忘。微軟利用物件導向的概念在設計C#語言時充分考慮了這個問題並很好地予以解決:把對象的
初始化工作放在建構函式中,把清除工作放在解構函式中。當對象被建立時,建構函式被自動執行。當對象消亡時,解構函式被自動執行。這樣就不用擔心忘記對象
的初始化和清除工作。
二.建構函式在C#中的運用
建構函式的名字不能隨便起,必須讓編譯器認得出
才可以被自動執行。它的命名方法既簡單又合理:讓建構函式與類同名。除了名字外,建構函式的另一個特別之處是沒有傳回值類型,這與傳回值類型為void的
函數不同。如果它有傳回值類型,那麼編譯器將不知所措。在你可以訪問一個類的方法、屬性或任何其它東西之前,
第一條執行的語句是包含有相應類的建構函式。甚至你自己不寫一個建構函式,也會有一個預設建構函式提供給你。
下面列舉了幾種類型的建構函式
1)預設建構函式
class TestClass
{
public TestClass(): base() {}
}
上面已介紹,它由系統(CLR)提供。
2)執行個體建構函式
執行個體建構函式是實現對類中執行個體進行初始化的方法成員。如:
using System;
class Point
{
public double x, y;
public Point()
{
this.x = 0;
this.y = 0;
}
public Point(double x, double y)
{
this.x = x;
this.y = y;
}
…
}
class Test
{
static void Main()
{
Point a = new Point();
Point b = new Point(3, 4); // 用建構函式初始化對象
…
}
}
聲明了一個類Point,它提供了兩個建構函式。它們是重載的。一個是沒有參數的Point建構函式和一個是有兩個double參數的Point構造函
數。如果類中沒有提供這些建構函式,那麼會CLR會自動提供一個預設建構函式的。但一旦類中提供了自訂的建構函式,如Point()和Point
(double x, double y),則預設建構函式將不會被提供,這一點要注意。
3) 靜態建構函式
靜態建構函式是實現對一個類進行初始化的方法成員。它一般用於對待用資料的初始化。靜態建構函式不能有參數,不能有修飾符而且不能被調用,當類被載入時,類的靜態建構函式自動被調用。如:
using System.Data;
class Employee
{
private static DataSet ds;
static Employee()
{
ds = new DataSet(...);
}
...
}
聲明了一個有靜態建構函式的類Employee。注意靜態建構函式只能對
待用資料成員進行初始化,而不能對非待用資料成員進行初始化。但是,非靜態建構函式既可以對待用資料成員賦值,也可以對非待用資料成員進行初始化。
如果類僅包含靜態成員,你可以建立一個private的建構函式:private TestClass() {…},但是private意味著從類的外面不可能訪問該建構函式。所以,它不能被調用,且沒有對象可以被該類定義執行個體化。
以上是幾種類型建構函式的簡單運用,下面將重點介紹一下在類的階層中(即繼承結構中)基類和衍生類別的建構函式的使用方式。衍生類別對象的初始化由基類和衍生類別共同完成:基類的成員由基類的建構函式初始化,衍生類別的成員由衍生類別的建構函式初始化。
當建立衍生類別的對象時,系統將會調用基類的建構函式和衍生類別的建構函式,構 造函數的執行次序是:先執行基類的建構函式,再執行衍生類別的建構函式。如果衍生類別又有對象成員,則,先執行基類的建構函式,再執行成員對象類的建構函式,最後執行衍生類別的建構函式。
至於執行基類的什麼建構函式,預設情況下是執行基類的無參建構函式,如果要執行基類的有參建構函式,則必須在衍生類別建構函式的成員初始化表中指出。如:
class A
{ private int x;
public A( ) { x = 0; }
public A( int i ) { x = i; }
};
class B : A
{ private int y;
public B( ) { y = 0; }
public B( int i ) { y = i; }
public B( int i, int j ):A(i) { y = j; }
};
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( ) { x = 0; },則在衍生類別的所有建構函式成員初始化表中必須指出基類A的有參建構函式A(i),如下所示:
class A
{ private int x;
public A( int i ) { x = i; }
};
class B : A
{ private int y;
public B():A(i) { y = 0; }
public B(int i):A(i) { y = i; }
public B(int i, int j):A(i) { y = j; }
};
三.解構函式和記憶體回收行程在C#中的運用
解構函式是實現銷毀一個類的執行個體的方法成員。解構函式不能有參數,不能任何修飾符而且不能被調用。由於解構函式的目的與建構函式的相反,就加首碼‘~’以示區別。
雖然C#(更確切的說是CLR)提供了一種新的記憶體管理機制---自動記憶體管理機制(Automatic memory
management),資源的釋放是可以通過“記憶體回收行程”
自動完成的,一般不需要使用者幹預,但在有些特殊情況下還是需要用到解構函式的,如在C#中非託管資源的釋放。
資源的釋放一般是通過"記憶體回收行程"自動完成的,但具體來說,仍有些需要注意的地方:
1. 實值型別和參考型別的引用其實是不需要什麼"記憶體回收行程"來釋放記憶體的,因為當它們出了範圍後會自動釋放所佔記憶體,因為它們都儲存在棧(Stack)中;
2. 只有參考型別的引用所指向的對象執行個體才儲存在堆(Heap)中,而堆因為是一個自由儲存空間,所以它並沒有像"棧"那樣有生存期("棧"的元素彈出後就代表生存期結束,也就代表釋放了記憶體),並且要注意的是,"記憶體回收行程"只對這塊地區起作用;
然而,有些情況下,當需要釋放非託管資源時,就必須通過寫代碼的方式來解決。通常是使用解構函式釋放非託管資源,將使用者自己編寫的釋放非託管資源的代碼
段放在解構函式中即可。需要注意的是,如果一個類中沒有使用到非託管資源,那麼一定不要定義解構函式,這是因為對象執行了解構函式,那麼"記憶體回收行程"在
釋放託管資源之前要先調用解構函式,然後第二次才真正釋放託管資源,這樣一來,兩次刪除動作的花銷比一次大多的。下面使用一段代碼來示解構函式是如何使用
的:
public class ResourceHolder
{
…
~ResourceHolder()
{
// 這裡是清理非託管資源的使用者程式碼片段
}
}
四.小結
建構函式與解構函式雖然是一個類中形式上較簡單的函數,但它們的使用決非看上去那麼簡單,因此靈活而正確的使用建構函式與解構函式能夠幫你更好的理解CLR的記憶體管理機制,以及更好的管理系統中的資源。
註:CLR
CLR(公用語言運行庫)和Java虛擬機器一樣也是一個運行時環境,它負責資源管理(記憶體配置和垃圾收集),並保證應用和底層作業系統之間必要的分離。
為了提高平台的可靠性,以及為了達到面向事務的電子商務應用所要求的穩定性層級,CLR還要負責其他一些任務,比如監視程式的運行。按照.NET的說法,
在CLR監視之下啟動並執行程式屬於“受管理的”(managed)代碼,而不在CLR之下、直接在裸機上啟動並執行應用或者組件屬於“非受管理的”
(unmanaged)的代碼。
CLR將監視形形色色的常見編程錯誤,許多年來這些錯誤一直是軟體故障的主要根源,其中包括:訪問數組元素越界,訪問未分配的記憶體空間,由於資料體積過大而導致的記憶體溢出,等等。
轉自:http://www.yesky.com/351/1755351_2.shtml