匿名
此帳號疑似異常
官方正在進行身份確認

#分享 Return by const lvalue reference in C++

2020年3月15日 16:38
以回文方式呈現,是因為這類 bug 不易察覺,事實上,我在原文第 7 樓的回應就是不正確的。 我建議先看完原文的問題以及討論串並仔細思考過後,再往下閱讀文章。 0. warm up - const lvalue reference 在設計函式參數的型態時,為了避免不必要的拷貝,我們可以採用 pass by lvalue reference 的方式來傳遞物件。 除此之外,為了讓函式能夠接收暫時物件或是字面常數,我們必須為參數加上 const 關鍵字。
這同時還可以避免我們不小心修改掉參數本身的值,事實上,這是還蠻常見的程式撰寫規範 e.g. Google C++ Style Guide """"" [1] Reference Arguments All parameters passed by lvalue reference must be labeled const. """"" 1. dangling references - case study std::max [2]
""""" [3] a temporary bound to a reference parameter in a function call exists until the end of the full expression containing that function call: if the function returns a reference, which outlives the full expression, it becomes a dangling reference. """"" 若一函式其參數為參考型態,且該參數綁定在一個 rvalue 上,則被參考到的記憶體物件「將會」隨著函式呼叫執行結束而被銷毀(就像一般的區域變數一樣)。 若一函式其參數為參考型態,且該參數綁定在一個 lvalue 上,則被參考到的記憶體物件「不會」隨著函式呼叫執行結束而被銷毀(函式參數只是一個變數別名)。 請密切注意下方範例中的參數 s 它是何時被生成的、何時被銷毀的。
未定義行為它就像是一顆不定時炸彈一樣,它被深埋上數千、數萬行程式碼裡,當它被執行到的時候,它可能會當場引爆、也可能不會,試著執行上面的程式碼,測試看看你會得到什麽結果。 2. 如何設計 API 來避免外部使用者犯錯 - case study facebook::folly::get_ref_default [4][5] 當函式的回傳型態為 const lvalue reference 時,直覺上我們會在呼叫端設定相同型態的變數來承接回傳值,來避免額外的拷貝成本,但誠如我們在上面所做的討論可知,這是有可能會造成未定義行為發生的,為了避免外部使用者不小心犯錯,我們可以透過函式重載以及關鍵字 delete 來排除帶入 rvalue 的情況,藉此讓編譯器報錯以提醒外部使用者,當前程式的寫法是不被允許的。
3. 如何偵錯 [5][6] 開啟編譯器選項: -O1 -fsanitize=address -fno-omit-frame-pointer
4. 參考資料(請使用網頁版瀏覽) [1]:
[2]:
[3]:
[4]:
[5]: Bug #3 12:46~17:06
[6]:
10
留言 2
文章資訊
Logo
每天有 6 則貼文
共 2 則留言
匿名
此帳號疑似異常
官方正在進行身份確認
延伸討論第 1 點 可能會有人會想說,既然不能直接回傳暫時物件,那我可不可以透過 new 運算子來拷貝一份物件,讓它被放在 heap memory 裡面?
這個答案是肯定的,但是,自行手動管理 heap memory 這在 modern C++ (C++11/14/17) 裡面是不受歡迎的。 聽到這裡,也許還會有人想到,那我可不可以使用 smart pointer 來管理 heap memory 裡面的物件?
這個答案是否定的,永遠都不要寫出以上程式碼,在 foo 裡面產生的 shared_ptr<S> 物件,會在函式結束時,因 reference counter 歸零,而自動回收被指向的記憶體物件,因此,變數 s 實際上是在參考一塊已經被釋放掉的記憶體內容 (dangling reference)。 bonus bug 28:29~29:07
元智大學
推推 很棒的觀念分享