作者eye5002003 (下一夜)
看板C_and_CPP
標題[問題] shared_ptr如何避免cyclic reference?
時間Sat Jun 15 17:10:10 2019
可以自由傳遞指標的 std::shared_ptr 比傳統指標要安全許多
但即使如此也還是有cyclic reference的問題存在
網路上查到的解法幾乎都用 weak_ptr 來處理
但是我怎麼看都不覺得這算解法
因為它無法阻止物件被釋放
之所以要使用 std::shared_ptr 就是希望抓著指標就一定能使用所指的物件
我自己目前的做法是對物件分層級
只有高層物件可以擁有指標指向下層物件
確保不會連成一圈
這方式我還沒看到明顯的問題
但是這種自我約束的行為還是很不可靠
一個不小心包成std::function之類的東西然後亂丟可能就發生
而且一旦出現cyclic也很難查覺
因為它就只是安靜的咬著記憶體不放
不知道有沒有更理想的處理方式?
或者有比 shared_ptr 更好的工具也可以介紹一下
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 118.167.53.99 (臺灣)
※ 文章網址: https://webptt.com/m.aspx?n=bbs/C_and_CPP/M.1560589812.A.F19.html
1F:→ loveme00835: 你怎麼不先從設計開始討論?會用到 std::shared_ptr 06/15 19:08
2F:→ loveme00835: 大部分情況就是懶得好好設計 06/15 19:08
不用shared_ptr的話,物件不就只能活在一個scope裡面而已了嗎?
另外std::function要有closure效果的話也是綁shared_ptr比較安全
很難不使用啊
※ 編輯: eye5002003 (118.167.53.99 臺灣), 06/15/2019 20:53:47
3F:→ loveme00835: 你有好好釐清 ownership 嗎? 06/16 00:00
4F:→ loveme00835: std::shared_ptr 的 share 是 share responsibility 06/16 00:02
5F:→ loveme00835: 不是 share object, 首先為了 share object 用 06/16 00:03
6F:→ loveme00835: std::shared_ptr 就算是誤用, 為了讓 std::function 06/16 00:04
7F:→ loveme00835: own object 你有必須這樣做的理由嗎? 還是 lifetime 06/16 00:04
8F:→ loveme00835: 還沒分析過就直接用了? 06/16 00:05
不管是share什麼,只要裡面含有shared_ptr就有可能出問題
如果自己約束share的記憶體不含指標的話確實也能避免這問題
但我沒試過就是了
我之所以會裝物件是為了讓物件之間的聯繫能更安全
只要抓著指標就一定能聯絡上目標物件
也不用去想聯絡對象已經被解構了該如何處理
※ 編輯: eye5002003 (118.167.54.239 臺灣), 06/16/2019 10:58:03
9F:→ loveme00835: 那首先就要問到, 為什麼你要連絡的對象會比你還早被 06/16 12:04
10F:→ loveme00835: 解構? 是不是你在基本上就無法掌握控制每個物件的生 06/16 12:05
11F:→ loveme00835: 命週期, 導致只能用最簡單的方法: 讓 shared_ptr 幫 06/16 12:06
12F:→ loveme00835: 你處理這些複雜事? 06/16 12:06
13F:→ loveme00835: 一般分享物件都是用指標/參考, 為什麼你要用智慧指標 06/16 12:12
14F:→ loveme00835: ? 這就是需要先討論的, 而不是先決定用智慧指標, 才 06/16 12:13
15F:→ loveme00835: 來解決設計不良的問題 06/16 12:13
理想是配置記憶體能遵守stack先進後出原則
這樣當然每個參考對象使用時都會存在
但是實戰上你處理好的數據不見得一離開scope就肯乖乖釋放
也許預期到未來還有可能會用到
所以保留下來日後可以避免重算一次
另外像是有互動功能的程式
需要依使用者的操作來判斷什麼資源可以先釋放
(例如瀏覽器就無法預知你會先關掉哪個分頁)
沒辦法那麼理想的去固定解構順序
所以不是什麼設計不良造成的
而是真的就是需要 shared_ptr
※ 編輯: eye5002003 (118.167.54.239 臺灣), 06/16/2019 13:10:04
16F:→ loveme00835: 那就是你在處理這些需要重用的資源時, 除了什麼scope 06/16 14:23
17F:→ loveme00835: 以外, 沒有想過其它的管理方式 06/16 14:24
18F:→ loveme00835: 舉個例子, 如果資源是和 task 作關聯, 如果作完就要 06/16 16:23
19F:→ loveme00835: 釋放, 那麼就可以讓 function object 來 own 資源, 06/16 16:23
20F:→ loveme00835: 在 dispath task 的時候, 把資源 move 出去讓特定的 06/16 16:24
21F:→ loveme00835: 物件去管理. 你可以嘗試把這種 own shared_ptr 的類 06/16 16:26
22F:→ loveme00835: 別/物件都畫一條線出來, 你會發現這樣的圖會很複雜, 06/16 16:26
23F:→ loveme00835: 所以在用智慧指標前, 你需要好好地思考是不是在不得 06/16 16:28
24F:→ loveme00835: 不用的情況下才寫, 還是懶得設計為了方便才寫. 大部 06/16 16:29
25F:→ loveme00835: 份情況都是 unique_ptr 就可以解掉的問題, 或許你可 06/16 16:30
26F:→ loveme00835: 以說說你不得不用的理由? 通常就是招式用盡才會出 06/16 16:31
27F:→ loveme00835: shared_ptr 06/16 16:31
28F:噓 KanzakiHAria: 一般軟工把這個現象稱為 "設計不良" 06/17 21:32
29F:→ Killercat: 你的case可以參考一下是否該使用std::unique_ptr 06/18 11:15
30F:→ Killercat: shared_ptr的確是有這種ownership(肇因於設計不良)不明 06/18 11:15
31F:→ Killercat: 的case,這種其實把權責劃分一下滿容易解決的 06/18 11:16
32F:→ Killercat: 很多人把shared_ptr當scope ptr在用 但是別忘了使用的 06/18 11:17
33F:→ Killercat: 時候,總要記得這ptr總該要有個ownership的 06/18 11:17
34F:→ Killercat: 像你預期未來會在用到 那你就該有個controller處理這個 06/18 11:18
35F:→ Killercat: 真的決定要cleanup時的邏輯 ownership要歸給該control 06/18 11:18
36F:→ Killercat: 請注意shared_ptr不是GC 不要老用GC觀點思考這問題 06/18 11:19
37F:→ Killercat: 你很多思考上都是把它當GC了 這兩個是完全不同的概念 06/18 11:20
38F:→ eye5002003: 確實unique_ptr才是謹慎的選擇 06/18 15:52
這影片有回答到兩個我一開始提到的疑慮
1.module要分層級來管制指標方向(我當前的策略)
2.別丟shared_ptr進std::function,要丟weak_ptr
shared_ptr用起來很方便
唯一要擔心的是它會串成一圈
最終還是只能自己注意管理
並節制使用,優先考慮使用unique_ptr
※ 編輯: eye5002003 (118.167.48.176 臺灣), 06/27/2019 08:17:37