不知不覺就直接來到第三章最後一個守則了!今天的守則雖然簡單,但乍看之下不會直接想到原因,書中詳盡解釋了其中的道理,讓人有輕鬆掌握到知識的感覺XD 就來看看吧~
今天的守則是:
Store
new
ed objects in smart pointers in standalone statements。
假設今天的情境是我們有下面兩個function:
int priority();
void processWidget(std::shared_ptr<Widget> pw, int priority);
深受前幾天守則宣導的我們,自然地知道pointer要採用物件來管理比較好!於是掏出最實用的RAII─shared_ptr
~來管理Widget
。那現在我們要call processWidget
這個function,要怎麼call呢?
首先初級版錯誤示範:
prcoessWidget(new Widget, prioirty()); // error
原因:這個function接受的input是shared_ptr
,而raw pointer要轉成shared_ptr
必須是explicit,沒有implicit的轉法。
以下是能動的版本:
processWidget(std::shared_ptr<Widget>(new Widget), priority());
一切看起來都很好,大家都有用object來管理資源,但其實這是陷阱!以上這個call法有可能會leak resource!
WHY?
且看大師娓娓道來:
首先,compiler要call processWidget
之前,會先計算傳入的arguments。
第一個引數std::shared_ptr<Widget>(new Widget)
包含了兩部分:
new Widget
的執行
shard_ptr
的constructor
外加第二個引數priorty()
是一個function call:
3. call priority
而C++的特性是對執行順序的自由度頗高,沒有像Java或C#對參數的計算有明確順序。這邊有必定前後關係的只有1需要在2前面,因為2需要1的結果。然而3這項,就不一定會是在一開始,或1跟2中間,或最後執行。如果compiler最後決定在中間call(有可能為了產生比較有效率的code所以這樣排),那順序就變成這樣:
new Widget
的執行
priority
shared_ptr
的constructor
可以看到new Widget
跟轉成shared_ptr
被拆開啦!這代表什麼?代表違反了RAII!(忘記RAII是什麼嗎?可以再看一遍─)
如果priority()
中途出了什麼差錯,那3就不會被執行,它就沒被存到shared_ptr裡面惹,那這個Widget
pointer就有leak的風險!(沒有被確保會被delete)
那解決方法也很簡單,保證不要讓別人插斷就行了,移除掉3這個不確定因子,就切合主題─請單獨call 1.2這兩件事!
像是這樣:
std::shared_ptr<Widget> pw(new Widget);
processWidget(pw, prioirty());
大功告成!
貼心重點提醒:
- Store
new
ed objects in smart pointers in standalone statements. Failure to do this can lead to subtle resource leaks when exceptions are thrown.
第三章就到此結束啦!讓我們期待第四章─Designs and declarations的精采內容,當然也可以回去複習一下到此為止的17條守則們([Day 1] 前言)~