- 相關(guān)推薦
C語(yǔ)言里面構造函數和析構函數的運用辦法
摘 要:構造函數與析構函數是一個(gè)類(lèi)中看似較為簡(jiǎn)單的兩類(lèi)函數,但在實(shí)際運用過(guò)程中總會(huì )出現一些意想不到的運行錯誤。本文將較系統的介紹構造函數與析構函數的原理及在C#中的運用,以及在使用過(guò)程中需要注意的若干事項。
關(guān)鍵字:構造函數;析構函數;垃圾回收器;非托管資源;托管資源
一.構造函數與析構函數的原理
作為比C更先進(jìn)的語(yǔ)言,C#提供了更好的機制來(lái)增強程序的安全性。C#編譯器具有嚴格的類(lèi)型安全檢查功能,它幾乎能找出程序中所有的語(yǔ)法問(wèn)題,這的確幫了程序員的大忙。但是程序通過(guò)了編譯檢查并不表示錯誤已經(jīng)不存在了,在“錯誤”的大家庭里,“語(yǔ)法錯誤”的地位只能算是冰山一角。級別高的錯誤通常隱藏得很深,不容易發(fā)現。
根據經(jīng)驗,不少難以察覺(jué)的程序錯誤是由于變量沒(méi)有被正確初始化或清除造成的,而初始化和清除工作很容易被人遺忘。微軟利用面向對象的概念在設計C#語(yǔ)言時(shí)充分考慮了這個(gè)問(wèn)題并很好地予以解決:把對象的初始化工作放在構造函數中,把清除工作放在析構函數中。當對象被創(chuàng )建時(shí),構造函數被自動(dòng)執行。當對象消亡時(shí),析構函數被自動(dòng)執行。這樣就不用擔心忘記對象的初始化和清除工作。
二.構造函數在C#中的運用
構造函數的名字不能隨便起,必須讓編譯器認得出才可以被自動(dòng)執行。它的命名方法既簡(jiǎn)單又合理:讓構造函數與類(lèi)同名。除了名字外,構造函數的另一個(gè)特別之處是沒(méi)有返回值類(lèi)型,這與返回值類(lèi)型為void的函數不同。如果它有返回值類(lèi)型,那么編譯器將不知所措。在你可以訪(fǎng)問(wèn)一個(gè)類(lèi)的方法、屬性或任何其它東西之前, 第一條執行的語(yǔ)句是包含有相應類(lèi)的構造函數。甚至你自己不寫(xiě)一個(gè)構造函數,也會(huì )有一個(gè)缺省構造函數提供給你。
class TestClass
{
public TestClass(): base() {} // 由CLR提供
}
下面列舉了幾種類(lèi)型的構造函數
1)缺省構造函數
class TestClass
{
public TestClass(): base() {}
}
上面已介紹,它由系統(CLR)提供。
2)實(shí)例構造函數
實(shí)例構造函數是實(shí)現對類(lèi)中實(shí)例進(jìn)行初始化的方法成員。如:
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); // 用構造函數初始化對象
}
}
聲明了一個(gè)類(lèi)Point,它提供了兩個(gè)構造函數。它們是重載的。一個(gè)是沒(méi)有參數的Point構造函數和一個(gè)是有兩個(gè)double參數的Point構造函數。如果類(lèi)中沒(méi)有提供這些構造函數,那么會(huì )CLR會(huì )自動(dòng)提供一個(gè)缺省構造函數的。但一旦類(lèi)中提供了自定義的構造函數,如Point()和Point(double x, double y),則缺省構造函數將不會(huì )被提供,這一點(diǎn)要注意。
3) 靜態(tài)構造函數
靜態(tài)構造函數是實(shí)現對一個(gè)類(lèi)進(jìn)行初始化的方法成員。它一般用于對靜態(tài)數據的初始化。靜態(tài)構造函數不能有參數,不能有修飾符而且不能被調用,當類(lèi)被加載時(shí),類(lèi)的靜態(tài)構造函數自動(dòng)被調用。如:
using System.Data;
class Employee
{
private static DataSet ds;
static Employee()
{
ds = new DataSet(...);
}
}
聲明了一個(gè)有靜態(tài)構造函數的類(lèi)Employee。注意靜態(tài)構造函數只能對靜態(tài)數據成員進(jìn)行初始化,而不能對非靜態(tài)數據成員進(jìn)行初始化。但是,非靜態(tài)構造函數既可以對靜態(tài)數據成員賦值,也可以對非靜態(tài)數據成員進(jìn)行初始化。
如果類(lèi)僅包含靜態(tài)成員,你可以創(chuàng )建一個(gè)private的構造函數:private TestClass() {…},但是private意味著(zhù)從類(lèi)的外面不可能訪(fǎng)問(wèn)該構造函數。所以,它不能被調用,且沒(méi)有對象可以被該類(lèi)定義實(shí)例化。
以上是幾種類(lèi)型構造函數的簡(jiǎn)單運用,下面將重點(diǎn)介紹一下在類(lèi)的層次結構中(即繼承結構中)基類(lèi)和派生類(lèi)的構造函數的使用方式。派生類(lèi)對象的初始化由基類(lèi)和派生類(lèi)共同完成:基類(lèi)的成員由基類(lèi)的構造函數初始化,派生類(lèi)的成員由派生類(lèi)的構造函數初始化。
當創(chuàng )建派生類(lèi)的對象時(shí),系統將會(huì )調用基類(lèi)的構造函數和派生類(lèi)的構造函數,構 造函數的執行次序是:先執行基類(lèi)的構造函數,再執行派生類(lèi)的構造函數。如果派生類(lèi)又有對象成員,則,先執行基類(lèi)的構造函數,再執行成員對象類(lèi)的構造函數,最后執行派生類(lèi)的構造函數。
至于執行基類(lèi)的什么構造函數,缺省情況下是執行基類(lèi)的無(wú)參構造函數,如果要執行基類(lèi)的有參構造函數,則必須在派生類(lèi)構造函數的成員初始化表中指出。如:
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(); //執行基類(lèi)A的構造函數A(),再執行派生類(lèi)的構造函數B()
B b2 = new B(1); //執行基類(lèi)A的構造函數A(),再執行派生類(lèi)的構造函數B(int)
B b3 = new B(0,1); //執行執行基類(lèi)A的構造函數A(int) ,再執行派生類(lèi)的
構造函數B(int,int)
在這里構造函數的執行次序是一定要分析清楚的。另外,如果基類(lèi)A中沒(méi)有提供無(wú)參構造函數public A( ) { x = 0; },則在派生類(lèi)的所有構造函數成員初始化表中必須指出基類(lèi)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#中的運用
析構函數是實(shí)現銷(xiāo)毀一個(gè)類(lèi)的實(shí)例的方法成員。析構函數不能有參數,不能任何修飾符而且不能被調用。由于析構函數的目的與構造函數的相反,就加前綴‘~’以示區別。
雖然C#(更確切的說(shuō)是CLR)提供了一種新的內存管理機制---自動(dòng)內存管理機制(Automatic memory management),資源的釋放是可以通過(guò)“垃圾回收器” 自動(dòng)完成的,一般不需要用戶(hù)干預,但在有些特殊情況下還是需要用到析構函數的,如在C#中非托管資源的釋放。
資源的釋放一般是通過(guò)"垃圾回收器"自動(dòng)完成的,但具體來(lái)說(shuō),仍有些需要注意的地方:
1. 值類(lèi)型和引用類(lèi)型的引用其實(shí)是不需要什么"垃圾回收器"來(lái)釋放內存的,因為當它們出了作用域后會(huì )自動(dòng)釋放所占內存,因為它們都保存在棧(Stack)中;
2. 只有引用類(lèi)型的引用所指向的對象實(shí)例才保存在堆(Heap)中,而堆因為是一個(gè)自由存儲空間,所以它并沒(méi)有像"棧"那樣有生存期("棧"的元素彈出后就代表生存期結束,也就代表釋放了內存),并且要注意的是,"垃圾回收器"只對這塊區域起作用;
然而,有些情況下,當需要釋放非托管資源時(shí),就必須通過(guò)寫(xiě)代碼的方式來(lái)解決。通常是使用析構函數釋放非托管資源,將用戶(hù)自己編寫(xiě)的釋放非托管資源的代碼段放在析構函數中即可。需要注意的是,如果一個(gè)類(lèi)中沒(méi)有使用到非托管資源,那么一定不要定義析構函數,這是因為對象執行了析構函數,那么"垃圾回收器"在釋放托管資源之前要先調用析構函數,然后第二次才真正釋放托管資源,這樣一來(lái),兩次刪除動(dòng)作的花銷(xiāo)比一次大多的。下面使用一段代碼來(lái)示析構函數是如何使用的:
public class ResourceHolder
{
~ResourceHolder()
{
// 這里是清理非托管資源的用戶(hù)代碼段
}
}
四.小結
構造函數與析構函數雖然是一個(gè)類(lèi)中形式上較簡(jiǎn)單的函數,但它們的使用決非看上去那么簡(jiǎn)單,因此靈活而正確的使用構造函數與析構函數能夠幫你更好的理解CLR的內存管理機制,以及更好的管理系統中的資源。
【C語(yǔ)言里面構造函數和析構函數的運用辦法】相關(guān)文章:
C語(yǔ)言函數的運用及調用05-10
C語(yǔ)言指針函數和函數指針詳解12-08
淺談如何運用C語(yǔ)言malloc和free函數08-09
C語(yǔ)言中isalnum()函數和isalpha()函數的對比03-14
C語(yǔ)言函數的遞歸和調用05-09
C語(yǔ)言的strcpy()和strncpy()函數06-04
C語(yǔ)言函數的聲明以及函數原型04-20