[問題] shared_ptr如何避免cyclic reference?

看板C_and_CPP作者 (下一夜)時間4年前 (2019/06/15 17:10), 4年前編輯推噓-1(0138)
留言39則, 5人參與, 4年前最新討論串1/2 (看更多)
可以自由傳遞指標的 std::shared_ptr 比傳統指標要安全許多 但即使如此也還是有cyclic reference的問題存在 網路上查到的解法幾乎都用 weak_ptr 來處理 但是我怎麼看都不覺得這算解法 因為它無法阻止物件被釋放 之所以要使用 std::shared_ptr 就是希望抓著指標就一定能使用所指的物件 我自己目前的做法是對物件分層級 只有高層物件可以擁有指標指向下層物件 確保不會連成一圈 這方式我還沒看到明顯的問題 但是這種自我約束的行為還是很不可靠 一個不小心包成std::function之類的東西然後亂丟可能就發生 而且一旦出現cyclic也很難查覺 因為它就只是安靜的咬著記憶體不放 不知道有沒有更理想的處理方式? 或者有比 shared_ptr 更好的工具也可以介紹一下 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 118.167.53.99 (臺灣) ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1560589812.A.F19.html

06/15 19:08, 4年前 , 1F
你怎麼不先從設計開始討論?會用到 std::shared_ptr
06/15 19:08, 1F

06/15 19:08, 4年前 , 2F
大部分情況就是懶得好好設計
06/15 19:08, 2F
不用shared_ptr的話,物件不就只能活在一個scope裡面而已了嗎? 另外std::function要有closure效果的話也是綁shared_ptr比較安全 很難不使用啊 ※ 編輯: eye5002003 (118.167.53.99 臺灣), 06/15/2019 20:53:47

06/16 00:00, 4年前 , 3F
你有好好釐清 ownership 嗎?
06/16 00:00, 3F

06/16 00:02, 4年前 , 4F
std::shared_ptr 的 share 是 share responsibility
06/16 00:02, 4F

06/16 00:03, 4年前 , 5F
不是 share object, 首先為了 share object 用
06/16 00:03, 5F

06/16 00:04, 4年前 , 6F
std::shared_ptr 就算是誤用, 為了讓 std::function
06/16 00:04, 6F

06/16 00:04, 4年前 , 7F
own object 你有必須這樣做的理由嗎? 還是 lifetime
06/16 00:04, 7F

06/16 00:05, 4年前 , 8F
還沒分析過就直接用了?
06/16 00:05, 8F
不管是share什麼,只要裡面含有shared_ptr就有可能出問題 如果自己約束share的記憶體不含指標的話確實也能避免這問題 但我沒試過就是了 我之所以會裝物件是為了讓物件之間的聯繫能更安全 只要抓著指標就一定能聯絡上目標物件 也不用去想聯絡對象已經被解構了該如何處理 ※ 編輯: eye5002003 (118.167.54.239 臺灣), 06/16/2019 10:58:03

06/16 12:04, 4年前 , 9F
那首先就要問到, 為什麼你要連絡的對象會比你還早被
06/16 12:04, 9F

06/16 12:05, 4年前 , 10F
解構? 是不是你在基本上就無法掌握控制每個物件的生
06/16 12:05, 10F

06/16 12:06, 4年前 , 11F
命週期, 導致只能用最簡單的方法: 讓 shared_ptr 幫
06/16 12:06, 11F

06/16 12:06, 4年前 , 12F
你處理這些複雜事?
06/16 12:06, 12F

06/16 12:12, 4年前 , 13F
一般分享物件都是用指標/參考, 為什麼你要用智慧指標
06/16 12:12, 13F

06/16 12:13, 4年前 , 14F
? 這就是需要先討論的, 而不是先決定用智慧指標, 才
06/16 12:13, 14F

06/16 12:13, 4年前 , 15F
來解決設計不良的問題
06/16 12:13, 15F
理想是配置記憶體能遵守stack先進後出原則 這樣當然每個參考對象使用時都會存在 但是實戰上你處理好的數據不見得一離開scope就肯乖乖釋放 也許預期到未來還有可能會用到 所以保留下來日後可以避免重算一次 另外像是有互動功能的程式 需要依使用者的操作來判斷什麼資源可以先釋放 (例如瀏覽器就無法預知你會先關掉哪個分頁) 沒辦法那麼理想的去固定解構順序 所以不是什麼設計不良造成的 而是真的就是需要 shared_ptr ※ 編輯: eye5002003 (118.167.54.239 臺灣), 06/16/2019 13:10:04

06/16 14:23, 4年前 , 16F
那就是你在處理這些需要重用的資源時, 除了什麼scope
06/16 14:23, 16F

06/16 14:24, 4年前 , 17F
以外, 沒有想過其它的管理方式
06/16 14:24, 17F

06/16 16:23, 4年前 , 18F
舉個例子, 如果資源是和 task 作關聯, 如果作完就要
06/16 16:23, 18F

06/16 16:23, 4年前 , 19F
釋放, 那麼就可以讓 function object 來 own 資源,
06/16 16:23, 19F

06/16 16:24, 4年前 , 20F
在 dispath task 的時候, 把資源 move 出去讓特定的
06/16 16:24, 20F

06/16 16:26, 4年前 , 21F
物件去管理. 你可以嘗試把這種 own shared_ptr 的類
06/16 16:26, 21F

06/16 16:26, 4年前 , 22F
別/物件都畫一條線出來, 你會發現這樣的圖會很複雜,
06/16 16:26, 22F

06/16 16:28, 4年前 , 23F
所以在用智慧指標前, 你需要好好地思考是不是在不得
06/16 16:28, 23F

06/16 16:29, 4年前 , 24F
不用的情況下才寫, 還是懶得設計為了方便才寫. 大部
06/16 16:29, 24F

06/16 16:30, 4年前 , 25F
份情況都是 unique_ptr 就可以解掉的問題, 或許你可
06/16 16:30, 25F

06/16 16:31, 4年前 , 26F
以說說你不得不用的理由? 通常就是招式用盡才會出
06/16 16:31, 26F

06/16 16:31, 4年前 , 27F
shared_ptr
06/16 16:31, 27F

06/17 21:32, 4年前 , 28F
一般軟工把這個現象稱為 "設計不良"
06/17 21:32, 28F

06/18 11:15, 4年前 , 29F
你的case可以參考一下是否該使用std::unique_ptr
06/18 11:15, 29F

06/18 11:15, 4年前 , 30F
shared_ptr的確是有這種ownership(肇因於設計不良)不明
06/18 11:15, 30F

06/18 11:16, 4年前 , 31F
的case,這種其實把權責劃分一下滿容易解決的
06/18 11:16, 31F

06/18 11:17, 4年前 , 32F
很多人把shared_ptr當scope ptr在用 但是別忘了使用的
06/18 11:17, 32F

06/18 11:17, 4年前 , 33F
時候,總要記得這ptr總該要有個ownership的
06/18 11:17, 33F

06/18 11:18, 4年前 , 34F
像你預期未來會在用到 那你就該有個controller處理這個
06/18 11:18, 34F

06/18 11:18, 4年前 , 35F
真的決定要cleanup時的邏輯 ownership要歸給該control
06/18 11:18, 35F

06/18 11:19, 4年前 , 36F
請注意shared_ptr不是GC 不要老用GC觀點思考這問題
06/18 11:19, 36F

06/18 11:20, 4年前 , 37F
你很多思考上都是把它當GC了 這兩個是完全不同的概念
06/18 11:20, 37F

06/18 15:52, 4年前 , 38F
確實unique_ptr才是謹慎的選擇
06/18 15:52, 38F

06/21 21:44, 4年前 , 39F
這影片有回答到兩個我一開始提到的疑慮 1.module要分層級來管制指標方向(我當前的策略) 2.別丟shared_ptr進std::function,要丟weak_ptr shared_ptr用起來很方便 唯一要擔心的是它會串成一圈 最終還是只能自己注意管理 並節制使用,優先考慮使用unique_ptr ※ 編輯: eye5002003 (118.167.48.176 臺灣), 06/27/2019 08:17:37
文章代碼(AID): #1T1BNqyP (C_and_CPP)
文章代碼(AID): #1T1BNqyP (C_and_CPP)