剛上班,就被大李找去了。“Henry,昨天對窗體的操作給你最大的體會 是什麼?”
“當然有體會,最深的印象就是VB.NET中類是無所 不在了,連窗體都成為了一個類。”我深有感觸地說。
“沒錯 ,類是我們用來構造VB.NET應用程序時的最基本的編程結構了。你也學習過最基 本的面向對象編程了,那麼你能告訴我,結構與類有什麼相似之處與不同之處嗎 ?”
“好的。”我口中回答著,心裡還是有點不以為然 ,“結構和類,都是對成員的封裝方式,但是類可以支持繼承 ……”
大李一邊點著頭,一邊聽我說,聽到我最後支 吾著沒了聲音才抬起頭“還有呢?”
“沒了。”我 這時才開始心中發慌。
“呵呵,相同之處我想你心中還是明白的, 它們都含有成員,包括構造函數、方法、屬性、字段、常量、枚舉和事件。都可 以實現接口,都有共享的構造函數。”
“對不起,最後那一句 ,都有構造函數是什麼意思?結構的構造函數我從來沒有自己定義過。 ”
大李立刻寫下了這一段代碼:
Structure SHenry Public x, y As Integer Public Sub New(ByVal x As Integer, ByVal y As Integer) Me.x = x Me.y = y End Sub End Structure Sub main() Dim H1 As SHenry = New SHenry() Dim H2 As SHenry = New SHenry(2, 1) End Sub
“真的呢,可以實現結構的構造函數!”我還一直 沒有注意過這個問題。“可是,你只定義過一個帶參數的構造函數,H2的實 例化我是明白的,可是H1怎麼也能通過編譯器檢測呢?”其實我心中更想問 的是結構怎麼也能實例化成對象,那和類真的很象呀。
“原因麼, ”大李推了推眼鏡,“每個結構都隱式地具有 Public 無參數實例構 造函數,該構造函數產生結構的默認值。所以你平時不寫構造函數,也一樣可以 New出一個結構來,對吧?事實上,我們反而在結構類型聲明中不可能聲明無參數 實例構造函數。只能聲明‘參數化’實例構造函數。 ”
“都可以用new來實例化,結構和類在內存分配上難道也是 一樣的嗎?”這個問題我一直挺不明白,正好借這個話題問一下。
“在這上面,差別可就大了。”看到大李喜笑顏開的樣子,我 就知道問到點子上了,立刻擺開架勢,作認真傾聽狀。
“簡單來說 ,結構是值類型,而類是引用類型。因此,結構使用堆棧分配,類使用堆分配。 ”
看到我迷茫的雙眼,大李笑了笑,在電腦上飛快地寫了個示例:
Class CHenry Public z As Integer = 0 ‘能對非靜態成員初始化也是一個區別 End Class Sub main() Dim H1 As SHenry = New SHenry(0, 2) '賦給H1.x=0 Dim H2 As SHenry = H1 H2.x = 26 Dim R1 As New CHenry() 'R1.z也是等於0 Dim R2 As CHenry = R1 R2.z = 26 Console.WriteLine("H1.x=" &H1.x&",H2.x="& H2.x) Console.WriteLine("R1.z= "&R1.z&",R2.value= "& R2.z) End Sub
“你看一下結果應該是什麼?”大李一邊說,一邊 運行了程序:
H1.x= 0, H2.x= 26 R1.z= 26, R2.value= 26
大李看著我瞪圓的雙眼,慢慢地說: “這就是值類型和引用類型的差別。結構的實例 H2.x 賦值並不影響H1.x, 這是因為雖然它們同屬於一種SHenry結構,但它們都有各自的存儲空間。相反, 給 R2.z賦值26後; 則會影響R1 和 R2 都引用的對象”
“說得 更清楚一點,類作為引用類型,是存儲在運行時的堆上,只能通過引用該存儲來 訪問它們,不能直接訪問。引用類型的變量總是包含該類型的值引用,或包含空 引用。空引用不引用任何內容;除分配空引用外,對空引用進行的任何操作都是 無效的。引用類型的變量賦值只會創建引用的一個副本,而不是所引用的值的副 本。它們實際上都是會指向同一塊存儲區的。”大李手指了指運行的結果。
“結構是直接存儲在堆棧上,要麼在數組中,要麼在另一個類型中 。當包含結構實例的位置被銷毀時,結構實例也會被銷毀。值類型總是可以直接 訪問。我們不能創建對值類型的引用,也不能引用已銷毀的值類型實例。值類型 的變量總是包含此類型的值。與引用類型不同,值類型的值不能為空引用,也不 能引用派生相近程度較大的類型的對象。值類型的變量賦值會創建所賦的值的副 本,當然會新開辟一塊內存區來保存值。”
“哦,我明白了。 它們還有什麼區別沒有?”我對結構和類的區別第一次有了深刻的感覺。
“當然有很多,比如所有的結構成員都默認為 Public,類變量和常 量默認為 Private,其他的類成員默認為 Public;結構成員不能聲明為 Protected,類成員可以;結構過程不能處理事件,類過程可以;結構變量聲明不 能指定初始值、New 關鍵字或數組初始大小,類變量聲明可以。”大李喝了 口水,停了一下,然後繼續說。
“結構從不終止,所以公共語言運 行庫從不在任何結構上調用 Finalize 方法;類可由垃圾回收器終止,垃圾回收 器會跟蹤未完成的引用直到某個特定的實例,當檢測到沒有剩下的活動引用時, 垃圾回收器將在類上調用 Finalize。”
“這個我可以理解, 因為結構是值類型,是由系統統一管理內存,不是引用,所以不會對內存造成危 害。”我接著說了兩句。
“還有,你剛才也提到了它們之間一 個很重要的區別:結構是不可繼承的,而類可以繼承。其實結構自身是從 ValueType 類隱式繼承下來的。數據類型可分為值類型和引用類型。值類型要麼 是堆棧分配的,要麼是在結構中以內聯方式分配的。引用類型是堆分配的。引用 類型和值類型都是從最終的基類 Object 派生出來的。當值類型需要充當對象時 ,就在堆上分配一個包裝,該包裝能使值類型看上去像引用對象一樣,並且將該 值類型的值復制給它。該包裝被加上標記,以便系統知道它包含一個值類型。這 個進程稱為裝箱,其反向進程稱為取消裝箱。裝箱和取消裝箱能夠使任何類型像 對象一樣進行處理。”
“哦,我明白為什麼結構也能被實例化 成對象了!”我心中喜不自禁。“類的繼承我在用C++和Java時也經常 使用,但是VB6是不支持繼承的,上次您提到VB.NET可以支持繼承了,它是怎麼做 到的呀!”