2016-11-03 39 views
6

我一直在看香草薩特的CppCon 2016的談話中,他舉了一個例子around 37 minutes in,這樣的事情:什麼是C++中的回調地獄,爲什麼它會泄漏內存?

void f(shared_ptr<T> & ptr) 
{ 
    obj.on_draw([=]() { ... } 
} 

然後他說,

我聽說它被稱爲回調地獄,在那裏你註冊一個回調和 它有一個強大的擁有者 - 它恰好是一個垃圾收集指針 但它是一個強大的擁有者 - 但你永遠不會擺脫它,它的 只是永遠存儲在那裏,現在對象將永遠不會消失。

所以他說它被稱爲回調地獄,它會泄漏物體。 但我不太明白這段代碼有什麼問題,以及它爲什麼會泄漏。有人可以向我解釋這一點嗎?

我已經看過其他人在stackoverflow上的答案,但他們似乎都是關於併發。

+0

必須談論內存所有權。由於C中沒有垃圾收集器,所以很難知道何時/誰必須釋放分配的對象。不過,'unique_ptr'可以提供幫助。 –

+0

如果它是公開的,請鏈接談話。無論是否,請說出幻燈片#或演示文稿中的什麼時間。 –

+0

@RawN:這不是無關緊要的,它只是沒有足夠的瞭解薩特博士所說的話,任何人都可以回答,而無需去查找和觀看整個演講。 –

回答

5

什麼赫布薩特談論的是循環引用。他促進了分層的系統,其中資源沒有被流傳下來的代碼,「達到了」

1層 - 擁有從(下方)2層的資源和對象

二層技術 - 不能老是有對第1層對象的強烈參考

這確保了,依賴關係圖不會獲得圓圈。因此,如果第1層釋放所有第2層對象,那麼所有資源都會被破壞。 爲什麼這很重要很容易:如果obj a對obj b有強引用,並且obj b有強引用,則從C++ Std庫進行的資源計數無法處理循環引用(無法重新計數)爲了obj a,那麼他們將永遠不會被釋放。

醜陋的真相是,如果圓圈可能通過不同作者的軟件模塊遍歷多個引用,這也是一個問題。如果沒有像圖層一樣的方案,你不能只看代碼並且說「沒有機會結束引用我從中調用的對象」。 Herb Sutter提出了一個約定,除非你知道實現,否則不應該調用可能使資源保持活躍的函數。

這並不是說你永遠不應該這樣做,但是如果你遵循一套規則,你可以在不知道系統深度的其他部分的情況下驗證每層甚至每個文件的代碼。否則,你必須找到函數(on_draw)可能採取的所有可能的路徑,以查看是否可能導致循環依賴 - 並且如果可能觸及的任何代碼中有任何更改,則必須再次執行該操作!

在這種情況下,「回調地獄」是特別有問題的,因爲它有點繞過類型系統(不可能只允許來自較低層的接口),並且回調可以做任何事情。

如果回調不保存對資源的引用,那麼請使用普通指針代替,這明確指出調用者他並不需要擔心泄漏。不是現在或將來。