說來慚愧,是因為我忘了到底要怎么正確的delete,然后查到了這個話題,然后見識了一場大佬們的討論,
辯題:C++ 里 delete 指標兩次會怎么樣?(后來被扭曲為:C++ delete之后到底要不要置空)
正方:C++ delete之后當然要置空了
反方:C++ delete之后不應該置空掉
首先是置空派的選手上場:
一直以來都是這么寫的,書上、老師都是這么說的,
接著是不置空派選手上場:
一派胡言!
很怕這種觀念又成為了某種教條,
作者:丁冬 鏈接:https://www.zhihu.com/question/38998078/answer/79321819 來源:知乎
著作權歸作者所有,商業轉載請聯系作者獲得授權,非商業轉載請注明出處,
舉個例子:
~scoped_ptr() // never throws
{
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
boost::sp_scalar_destructor_hook( px );
#endif
boost::checked_delete( px );
}
這是boost::scoped_ptr的實作,checked_delete只是增加了對incomplete type的檢查:template inline void checked_delete(T * x)
{
// intentionally complex - simplification causes regressions
typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
(void) sizeof(type_must_be_complete);
delete x;
}
可以看見boost::scoped_ptr根本沒有對delete后的指標進行置空,如果boost::scoped_ptr真的把其持有的指標置空,反而可能掩蓋類似這樣的錯誤:
boost::scoped_ptr<MyClass> sp(new MyClass);
// some code
sp.~boost::scoped_ptr<MyClass>();
// by the end of the scope, sp counld be destructed again
按理說任何一個非trivial的有效物件被多次析構都應該是明顯的邏輯錯誤,構造和析構必須是一一對應的,這樣的錯誤也許一般用戶很少遇到,因為顯式呼叫解構式往往都是庫作者干的事,但這不代表這種奇怪的錯誤完全不會發生,很不幸的是,對于這種邏輯錯誤開發者往往沒有特別好的手段可以規避掉,二次delete一個懸垂指標行為是未定義的,也就是說錯誤是有可能被隱藏的,但是如果boost::scoped_ptr幫你把px給置空了,結果只會更糟糕:這下錯誤鐵定是被徹底隱藏了,根本別想找輕易到,沒有置空的話好歹有一定概率會崩潰給你看呢,當然“delete后置空指標”這種教條能流傳這么久,肯定是有它的道理的,
關于到底什么時候需要置空指標,關鍵之處在于搞清楚置空指標到底解決了什么問題,
先來理一下nullptr和野指標/懸垂指標的區別:
解參考:
nullptr:未定義
野指標/懸垂指標:未定義
delete nullptr:良好定義,delete什么也不用做
野指標/懸垂指標:未定義
值:nullptr:明確
野指標/懸垂指標:未定義,無法確定
可以發現nullptr最大的優勢在于值是明確的,也就是說分辨一個指標是不是nullptr比分辨一個指標是不是野指標/懸垂指標要容易得多,那delete后置空指標的最大價值就在于明確資源當前狀態,你想判斷一個資源是否有效時,你當然沒法直接跑去看這個資源在不在,而是得詢問資源的持有者是否仍然持有這個資源,如果所有被delete的指標都被置為nullptr,以后再去訪問這個指標的時候,我們可以通過其與nullptr的比較輕松判斷出資源是否已經被delete,當然,這個優勢基于一個重要的前提:在設計上允許在資源已經失效的情況下,資源的持有者保持有效,如果資源的持有者也被干掉了,那即使你想通過nullptr判斷資源是否存在,你也找不到持有資源的指標進行比較,至此,我們至少可以得出一個結論,如果物件是和持有其的指標一同銷毀的,那置空指標就是脫褲子放屁,這個結論還可以引申一下:如果資源與其所有的持有者(含弱參考)一同被銷毀,那即將消亡的持有者們都沒有必要,也沒有能力為資源的后續狀態負責,/********************************/其實delete/free后置空這樣的教條已經幾乎走上了和goto-label之流一樣的道路,很多人看到了前輩們留下的經驗之談,妄圖死記住口口相傳的best-practice,卻忘記了前因后果,
接下來插入一則訊息,中立派登場:
試一試怎么了,死的是程式,又不會是系統、電腦、或開發者,以后路還長著,連這個最簡單最基本的都不敢試的話,以后會遇到更多麻煩,另一方面,你不能通過一次試的結果得出結論,因為那只能說明在特定編譯器、特定crt下的結果,原理上你得知道delete是不改變指標值的,所以第二次delete的時候,行為未定義,什么事情都可能發生,好習慣永遠是delete之后立刻賦nullptr,這樣即便意外第二次delete了,也沒關系,因為delete nullptr是有良好定義的,
作者:叛逆者
鏈接:https://www.zhihu.com/question/38998078/answer/79188320
來源:知乎 著作權歸作者所有,商業轉載請聯系作者獲得授權,非商業轉載請注明出處,
我就是中立派的,我也去試了一下,結果我的VS給搞崩了,,,,
然后我又換到g++去試,最后發現:

該報錯的它就是要報錯,攔不住的,
就算二次置空了又怎么樣?
這里我要講一下,delete回收的是指標指向的那塊記憶體,而上面的p、q指向的是同一塊記憶體,
接下來又來了個正方觀點,我覺得他這個觀點挺烏龍的,因為我上面那個代碼就是受他的啟發:
作者:二律背反
鏈接:https://www.zhihu.com/question/38998078/answer/79157526
來源:知乎 著作權歸作者所有,商業轉載請聯系作者獲得授權,非商業轉載請注明出處,
Why doesn’t delete zero out its operand?Consider
delete p;
// ...
delete p;
If the … part doesn’t touch p then the second “delete p;” is a serious error that a C++ implementation cannot effectively protect itself against (without unusual precautions). Since deleting a zero pointer is harmless by definition, a simple solution would be for “delete p;” to do a “p=0;” after it has done whatever else is required. However, C++ doesn’t guarantee that.One reason is that the operand of delete need not be an lvalue. Consider:
delete p+1;
delete f(x);
Here, the implementation of delete does not have a pointer to which it can assign zero. These examples may be rare, but they do imply that it is not possible to guarantee that any pointer to a deleted object is 0.'' A simpler way of bypassing thatrule’’ is to have two pointers to an object:
T* p = new T;
T* q = p;
delete p;
delete q; // ouch!
C++ explicitly allows an implementation of delete to zero out an lvalue operand, and I had hoped that implementations would do that, but that idea doesn’t seem to have become popular with implementers.If you consider zeroing out pointers important, consider using a destroy function: template inline void destroy(T*& p) { delete p; p = 0; }
Consider this yet-another reason to minimize explicit use of new and delete by relying on standard library containers, handles, etc.Note that passing the pointer as a reference (to allow the pointer to be zero’d out) has the added benefit of preventing destroy() from being called for an rvalue:
int* f();
int* p;
// ...
destroy(f()); // error: trying to pass an rvalue by non-const reference
destroy(p+1); // error: trying to pass an rvalue by non-const reference
======================= c++ primer 4th edition ======================
5.11.6:After deleting a pointer, the pointer becomes what is referred to as a dangling pointer . A dangling pointer is one that refers to memory that once held an object but does so no longer. A dangling pointer can be the source of program errors that are difficult to detect. Setting the pointer to 0 after the object it refers to has been deleted makes it clear that the pointer points to no object.
我現在實在是不知道他到底要表達什么意思了,,,
好,反方大佬出場:
delete 之后賦值 nullptr 絕對是壞習慣,會掩蓋真實的錯誤,也不利于使用各種 memory checker 工具找出錯誤,類似的還有為了防止 double free 而在 free 之后賦值 NULL,一樣是錯誤的,在 C++ 里,任何資源釋放的操作都應該在解構式里進行,這樣只要管好物件生命期就不會有資源泄漏了,
作者:陳碩
鏈接:https://www.zhihu.com/question/38998078/answer/114314884
來源:知乎 著作權歸作者所有,商業轉載請聯系作者獲得授權,非商業轉載請注明出處,
是吧,大佬都這么說了,
又有反方大佬出場:
delete 后置為 nullptr 在 C++ 中一般是不必要的,因為 C++ 可以用 RAII 來管理記憶體,而解構式是被編譯器保證只執行一次的,
不過 free 后置為 NULL 在 C 中似乎常常是不可避免的,因為需要用 NULL 來判斷狀態,不然代碼就會寫得非常復雜,
最后呢,我的觀點:
關于到底什么時候需要置空指標,關鍵之處在于搞清楚置空指標到底解決了什么問題,
如果沒有必要,那就,放著唄,真錯了,錯了就是錯了,早點報出來早點解決掉,
此外,智能指標真的要去嘗試使用一下了,
精彩不容錯過:https://www.zhihu.com/question/38998078
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/292995.html
標籤:其他
上一篇:nginx問題的影響
