[問題] class destructor

看板C_and_CPP作者 (好馬兒)時間7年前發表 (2017/10/08 12:45), 7年前編輯推噓3(3040)
留言43則, 6人參與, 7年前最新討論串1/1
▉▂開發平台(Platform): (Ex: Win10, Linux, ...) MacOSX 編譯器(Ex: GCC, clang, VC++...)+目標環境(跟開發平台不同的話需列出) g++5.4 問題(Question): 大家好,最近在修資料結構這門課,過去雖然有修過計算機程式 ,但是兩學分的課所以感覺學得不是很扎實,對於class的部分只有粗略帶過.. 所以來這邊請教大家: 對於一個data member中含有pointer的class,初始時會用new配置記憶體, 而自行建立的destructor會用delete釋放配置給該指標的記憶體,請問這樣的狀況下 該怎麼在member function中回傳一個local的class變數呢? 因為函數功能的需要,必須在函數中宣告一個class變數a來承接運算的結果,並return 該變數給calling function,由calling function中的class變數b承接,但是這樣的情 況下,由於member function terminates, destructor將a裡面的pointer給delete掉, 造成calling function中b裡面的指標指向一個直隨時有可能被修改的地方... 有自己試著去overload operator=及建立copy constructor來看看是否可以讓calling function 中b裡面的pointer指向一個新的地方,在複製a的值過去,但好像a會先被 destructed(?!).. 上網查了很多destructor, return object的關鍵字,都找不到想要的答案.. 在這裡麻煩大家解答了.. 在下面附上class 的程式碼,大致上是我在建立一個stack的class 有一個destructor: ~Stack(){delete [] stack 初始的constructor有用new給該指標動態配置記憶體 問題卡在是我要寫一個member function splitStack(),作用是要將某個stack從中間 某個點分開拆成兩個stack,所以這個function 必須return stack型態的variable, 但是splitStack()中宣告的first再回傳值給main後,就被解構了.. 程式碼(Code):(請善用置底文網頁, 記得排版) http://codepad.org/IjGU1cDW -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 114.47.182.50 ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1507466726.A.1A7.html

10/08 21:07, 7年前 , 1F
是要定義copy constructor沒錯 看看有沒有哪裡寫錯
10/08 21:07, 1F

10/08 21:08, 7年前 , 2F
發現個奇怪的地方 destructor不應該手動呼叫
10/08 21:08, 2F

10/08 22:07, 7年前 , 3F
請問j大 copy constructor該怎麼寫呢?因為我造我的邏
10/08 22:07, 3F

10/08 22:08, 7年前 , 4F
輯寫還是沒辦法欸..附上我copy constructor的程式碼:
10/08 22:08, 4F

10/08 22:17, 7年前 , 5F

10/08 22:20, 7年前 , 6F
順便附上main的程式碼http://codepad.org/TcOdcyCc
10/08 22:20, 6F

10/08 22:21, 7年前 , 7F
我用一步步執行發現,雖然copy function不會讓兩個
10/08 22:21, 7F

10/08 22:22, 7年前 , 8F
POINTER 直接相等,但是splitStack一結束,居然會
10/08 22:22, 8F

10/08 22:22, 7年前 , 9F
兩個指標都destruct...= =,有人可以解釋原理嗎QQ
10/08 22:22, 9F

10/08 22:40, 7年前 , 10F
operator=也要留吧
10/08 22:40, 10F

10/08 22:42, 7年前 , 11F
copy ctor 的參數也許也要<T>嗎? 有點忘了
10/08 22:42, 11F

10/08 23:20, 7年前 , 12F
怎麼寫完 copy ctor 後 operator = 就不見了XD?
10/08 23:20, 12F

10/08 23:20, 7年前 , 13F
1. copy constructor 裡面不用 delete,因為是初始化,
10/08 23:20, 13F

10/08 23:20, 7年前 , 14F
還沒有前值,直接 new 然後 copy 就好。
10/08 23:20, 14F

10/08 23:20, 7年前 , 15F
2. copy assignment 需要檢查並釋放(或重新利用)現有
10/08 23:20, 15F

10/08 23:21, 7年前 , 16F
資源,同時要避免 self-assignment 造成錯誤的
10/08 23:21, 16F

10/08 23:21, 7年前 , 17F
delete。
10/08 23:21, 17F

10/08 23:21, 7年前 , 18F
3. copy assignment 請傳回 reference,不要傳值。
10/08 23:21, 18F

10/08 23:21, 7年前 , 19F
4. splitStack() return first 就好,不用再包一層。
10/08 23:21, 19F

10/08 23:21, 7年前 , 20F
其實你的指標幾乎都是在 3. 或 4. 多餘的臨時物件中被
10/08 23:21, 20F

10/08 23:22, 7年前 , 21F
delete 掉的。不過如果有做好 deep copy,頂多就是多跑
10/08 23:22, 21F

10/08 23:22, 7年前 , 22F
幾次 copy 效能較差而已,也不至於會使用到被 delete
10/08 23:22, 22F

10/08 23:22, 7年前 , 23F
掉的指標,造成嚴重錯誤。
10/08 23:22, 23F
喔喔喔瞭解了,真的是犯傻才會把operator=又刪掉XDD,現在程式可以work了, 非常感謝j大跟s大的解說! 不過想另外請教s大,關於第三點,為什麼會說傳回reference呢?是因爲傳回值 有什麼缺點嗎?謝謝。 ※ 編輯: s5031588 (114.47.182.50), 10/09/2017 00:44:19

10/09 01:04, 7年前 , 24F
傳回reference 是要允許使用 move 來減少一次 copy 吧
10/09 01:04, 24F

10/09 05:57, 7年前 , 25F
傳回值的話,比較容易產生不必要的臨時物件。除此之外,
10/09 05:57, 25F

10/09 05:57, 7年前 , 26F
還可能導致非使用者預期的行為。其實 C++ 並沒有硬性規
10/09 05:57, 26F

10/09 05:57, 7年前 , 27F
定 copy assignment 要傳回 reference。這比較像是一種
10/09 05:57, 27F

10/09 05:58, 7年前 , 28F
慣例,讓自定義類別的行為盡可能相似於 C++ 原生型別
10/09 05:58, 28F

10/09 05:58, 7年前 , 29F
(如 int)的行為。減少類別使用時產生意料之外的效果。
10/09 05:58, 29F

10/09 05:58, 7年前 , 30F
另外一個理由是,CopyAssignment requirements 要求回傳
10/09 05:58, 30F

10/09 05:58, 7年前 , 31F
型態必須是 T&,沒有滿足這個條件可能無法正常使用某些
10/09 05:58, 31F

10/09 05:58, 7年前 , 32F
標準容器的方法。事實上,如果沒有為類別宣告這個函數,
10/09 05:58, 32F

10/09 05:58, 7年前 , 33F
編譯器自動幫你生成的版本也是傳回 reference。當然,如
10/09 05:58, 33F

10/09 05:58, 7年前 , 34F
果你有更好的理由傳回新物件或者是其他型別,那就不用客
10/09 05:58, 34F

10/09 05:59, 7年前 , 35F
氣的傳吧!只要確保使用者了解並正確使用你的類別即可。
10/09 05:59, 35F
謝謝s大與l大的回覆,原來回傳值會產生一個臨時物件,一直以為回傳的值會直接 回到main,由main裡面的物件去接收...看來我原本誤會很深 再次感謝三位的回覆。 ※ 編輯: s5031588 (49.215.209.133), 10/09/2017 09:16:04

10/09 10:02, 7年前 , 36F
順便學一下什麼是rvalue reference與move吧XD
10/09 10:02, 36F
回james:好的我會研究看看,謝謝建議。 照著大家說得下去改了程式碼:http://codepad.org/RYcz2e7B main:http://codepad.org/cM17bFfK 為什麼我原本用b=a.splitStack(1)的時候,當等式結束的時候splitStack()裡面的first 會被destructor給刪除,然而改成上面main裡面的方式用Stack b(a.splitStack(1))時, 卻沒有呼叫destructor來解構first的跡象...? 另外,為什麼用copy assignment的時候,似乎是等到等號結束時,程式才會呼叫 destructor來解構first,而不是splitStack一結束就呼叫呢? 我好亂啊..QQ ※ 編輯: s5031588 (49.215.209.133), 10/09/2017 10:52:07

10/09 13:50, 7年前 , 37F
如果你想要預先allocate一些memory來用,你需要知道
10/09 13:50, 37F

10/09 13:50, 7年前 , 38F
placement new才行。今天destructor沒問題是因為T是int
10/09 13:50, 38F

10/09 13:51, 7年前 , 39F
建議先看gcc或msvc的vector怎麼實作vector,再實作stack
10/09 13:51, 39F

10/09 20:09, 7年前 , 40F
實測了一下,解構都正常喔。如果你有開最佳化(或者是
10/09 20:09, 40F

10/09 20:09, 7年前 , 41F
IDE 的 Release mode),那 first 可能被 RVO 掉,實際
10/09 20:09, 41F

10/09 20:09, 7年前 , 42F
解構會在 b 生命週期結束時(離開 main() 時)發生。
10/09 20:09, 42F

10/09 20:09, 7年前 , 43F
另外,複製建構函數裡的 new 括號錯了。
10/09 20:09, 43F
謝謝c大的回答,我會再參考看看。 回s大:我不知道我有沒有開最佳化欸,我不管在xcode裡面跑(似乎是debug mode 我也不知道怎麼看,整個IDE我只會有按過run跟新增專案而已..慘)還是用終端機跑 g++-5 main.cpp的指令出來的結果就是destructor只被呼叫了兩次(最後離開main的時 候),後來一步步看,似乎跑的時候系統一開始就直接把first.stack與b.stack指向 同一個地方了,所以最後destructor只有呼叫兩次,一次是delete a.stack 另一次是delete b.stack(first.stack),似乎這就是s大你說的first被RVO.. 再次感謝s大,還讓你幫我跑一次~ ※ 編輯: s5031588 (36.236.99.33), 10/10/2017 12:16:25
文章代碼(AID): #1PsXtc6d (C_and_CPP)