1. 程式人生 > >弱引用與強引用

弱引用與強引用

引用節省記憶體並不是真正的原因,真正的原因是有些物件如果被複制在現實中是不合事實的。


為什麼有引用計數
    C++中存在兩種語義:值語義(value sematics)和物件語義(object sematic),物件語義也可以叫做引用語義(reference sematics)。
值語義,指的是物件的拷貝與原物件無關,就像拷貝int一樣,C++的常用型別資料等都是值語義。
物件語義,指的是面向物件意義下的物件,是禁止拷貝的。
    在設計一個類的時候該類是否可以被拷貝(即具備拷貝建構函式),取決於拷貝後的語義是否成立,比如一個Thread類,拷貝後系統中並不會啟動另外一個執行緒,所以拷貝是禁止的。同樣類似於Employee僱員類也是。
    這麼設計起碼有兩個好處:
    1. 語義合理,有些物件複製是不符合常理的
    2. 節省記憶體

強引用:可以實現變數替換

弱引用:不完成變數替換


強引用
當物件被建立時,計數為1;每建立一個變數引用該物件時,該物件的計數就增加1;當上述變數銷燬時,物件的計數減1,當計數為0時,這個物件也就被析構了。
強引用計數在很多種情況下都是可以正常工作的,但是也有不湊效的時候,當出現迴圈引用時,就會出現嚴重的問題,以至於出現記憶體洩露,如下程式碼:

執行該程式可以看到,即使退出了test函式後,由於parent和children物件互相引用,它們的引用計數都是1,不能自動釋放,並且此時這兩個物件再無法訪問到。這就引起了c++中那臭名昭著的記憶體洩漏。
一般來講,解除這種迴圈引用有下面有三種可行的方法:
1. 當只剩下最後一個引用的時候需要手動打破迴圈引用釋放物件。
2. 當parent的生存期超過children的生存期的時候,children改為使用一個普通指標指向parent。
3. 使用弱引用的智慧指標打破這種迴圈引用。
雖然這三種方法都可行,但方法1和方法2都需要程式設計師手動控制,麻煩且容易出錯。


弱引用不更改引用計數,類似普通指標,只要把迴圈引用的一方使用弱引用,即可解除迴圈引用。

最後值得一提的是,雖然通過弱引用指標可以有效的解除迴圈引用,但這種方式必須在程式設計師能預見會出現迴圈引用的情況下才能使用,也可以是說這個僅僅是一種編譯期的解決方案,如果程式在執行過程中出現了迴圈引用,還是會造成記憶體洩漏的。因此,不要認為只要使用了智慧指標便能杜絕記憶體洩漏。畢竟,對於C++來說,由於沒有垃圾回收機制,記憶體洩漏對每一個程式設計師來說都是一個非常頭痛的問題。