我們都知道,c++最令人頭疼的問題也是被其他語言鄙視的問題——指針管理。而引用技術能夠讓上面的簡化了不少。下面說說c++引用計數的設計原理。
引用計數的兩個動機:
1. 簡化heap對象周邊的記錄工作。對于配置設定在heap中得記憶體,如果沒有顯式delete它,那麼就會常駐在記憶體區間,引起記憶體浪費。而且在heap對象的所有權會随着複制和指派出現轉移,是以編譯器還需要記錄對象的所有者是誰,這絕對是件吃力不讨好的事情。有了引用計數,對象使用該技術,對象便擁有它自己。一旦不再有任何人使用它,它便自動銷毀。這種技術最終建構出了垃圾回收機制(多說一句,這種技術在遊戲開發過程中一天都沒有用)。
2.引用計數的最重要功能是對象共享。當有許多對象有相同的值時,将該值存儲多次是一件愚蠢的事。是以讓所有等值對象共享一份實值即可滿足要求,這樣既節省記憶體空間,也讓速度加快(構造、析構對象費時)。
雖然引用技術能夠帶來足夠的便利,但它也會産生一些令人難過的影響。舉個例子吧!
假如有3個對象共享一個實值對象,即a、b、c都指向“hello ,world”字元串。現在我們想要修改a的值,注意這裡隻是需要修改a的值,而b、c的值不變,這時引用計數會讓你無所适從。一改都改,不改也不能達到目的。怎麼辦?
我們需要追蹤引用計數的對象有多少個對象共享它。如果有一個共享對象修改做出修改時,我們不能改變引用計數對象,因為還有其他共享對象需要它。這是引用計數的數值開始派上用場了。這也是引用計數不得不添加的開銷。亦即我們需要存儲所共享的對象,也需要儲存該對象的引用次數,兩者是一個耦合關系。
引用技術的實作方式是采用一個資料結構,其中含有引用次數和實際的資料對象。該資料結構将“某特定對象”以及“共享該對象的對象個數”關聯起來。但是引用計數有一個例外:分開構造,但擁有相同初值的對象,不會共享該資料結構,是以各自的引用計數沒有關系。例如:
string s1("hello world");
string s2("hello world");
s1和s2有着相同的string初值,由于其是分開構造,是以不會共享資料結構,即其引用計數都為1.而下面的代碼:
string s1("hello world");
string s2 = s1;
如果将s2指派s1,那麼兩者的構造過程一緻,此時共享資料結構,那麼s2指派會導緻引用計數+1。
當然我們也可以實作隻要是指向相同的對象就使用引用計數技術,即僅僅在“面對真正獨一無二的字元串時”才産生新的對象,否則都共享原有的對象。不過這樣子代價太大,一般不提倡。