天天看點

《C++面向對象高效程式設計(第2版)》——4.2 無用單元收集問題

本節書摘來自異步社群出版社《c++面向對象高效程式設計(第2版)》一書中的第4章,第4.2節,作者: 【美】kayshav dattatri,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

c++面向對象高效程式設計(第2版)

在我們讨論無用單元收集1(garbage collection)之前,先了解一下何為無用單元(garbage),何為懸挂引用(dangling reference)。

所謂無用單元(garbage),是一塊存儲區(或資源),該存儲區雖然是程式(或程序)的一部分,但是在程式中卻不可再對其引用。按照c++的規定,我們可以說,無用單元是程式中沒有指針指向的某些資源。以下是一個示例:

main()

{

   char *p;

   char *q;

   p = new char[1024]; // 配置設定1k字元的動态數組

   // ... 使用它

   q = p;  // 指針别名(pointer aliasing)

   delete [] p;

   p = 0;

   // 現在q是一個懸挂引用,如果試圖 *q = ‘a’,将導緻程式崩潰。

}<code>`</code>

如果試圖通路q所指向的記憶體,将引發嚴重的問題。在該例中,指針q稱為懸挂引用。指針别名(即多個指針持有相同的位址)通常會導緻懸挂引用。與無用單元相比,懸挂引用對于程式而言是緻命的,因為它必定導緻嚴重破壞(大多數可能是運作時崩潰)。

這兩個問題(無用單元和懸挂引用)都是操縱指針和指針别名直接導緻的結果。由于程式員複制了位址,但尚未了解複制位址的語義(和後果),才引發了這些問題。這不是oop才有的新問題,但oop讓這些問題的影響更加嚴重。

smalltalk:

一些語言提供自動的無用單元收集。在smalltalk環境下工作的程式員根本無需擔心無用單元,因為無用單元收集在smalltalk中是自動進行的。語言會跟蹤對記憶體的引用,當不再引用某塊記憶體時,語言便自動釋放它們。

eiffel:

eiffel以輔助程式的形式提供自動的無用單元收集,該程式定期在背景運作,用于收集所有不可再通路的單元。

c++:

c++不提供自動的無用單元收集機制。它支援所有類型的指針變量。這就把無用單元收集的重任留給了程式員。一般而言,這是存儲區管理的問題。在c++中,無用單元收集是一個研究課題。也許在不久的将來,c++也會有自動的無用單元收集。

由此可見,隻要不讓程式員建立持有記憶體區域位址的指針類型,幾乎就可以避免懸挂引用的問題。在eiffel、smalltalk和java中就是這種的情況。

你可能覺得奇怪,無用單元收集和懸挂引用在其他類型的程式設計中也會出現,為何要将這兩個問題作為oop中的特殊問題?請繼續往下讀。在面向過程程式設計系統中,沒有對象的概念,也不會頻繁地進行記憶體配置設定(和釋放)。然而,在oop中,一切皆為對象,而且絕大多數大型對象都要配置設定資源。在我們感興趣的面向對象系統中,時刻都有成百上千的對象,對象不斷地被建立、複制和銷毀。而且,可以按不同的方式,甚至動态地建立對象。是以,作為類的實作者,不僅要充分了解無用單元收集問題,還要額外注意存儲區的管理。

語言(自動或程式員實作)支援的無用單元收集類型,和語言本身的設計原理有較大的關系。提供自動無用單元收集的語言(如eiffel和smalltalk),實際上是基于引用的語言。在基于引用的語言中,每個對象隻是一個引用。當建立對象時,事實上是建立了一個引用,該引用持有真正對象的位址,此位址被儲存在别處。這使得複制和共享對象非常容易和迅速。但是,另一方面,這也導緻安全性較低。因為通過使用對象的引用,可能會意外地修改該對象。

然而,c++是一種基于值的語言(c也是)。在該語言中,一切(對象和基本類型)皆為值。每個對象都是一個真正的對象,不是一個指向儲存在别處的對象的指針。c++對待類和基本類型一樣,這是該語言中的統一模型。

eiffel使用雙重方案。在eiffel中,所有對象都基于引用,但所有基本類型都基于值。新對象獲得自己所有基本執行個體變量的副本,但是,在新對象中隻能包含對對象的引用。在其他地方也提到,引用要麼是void,要麼是一個對有效對象的引用。

另外,smalltalk對待對象和基本類型一緻。在該語言中,一切皆為對象,所有的基本類型也是對象。這使得語言易于了解,無需區分對象和基本類型的不同。

以下的示例說明了多種語言間的不同。回顧tcar類的例子:

繼續閱讀