[問題] 回傳值的消失

看板C_and_CPP作者 (無良記者)時間12年前 (2013/04/13 14:00), 編輯推噓3(3076)
留言79則, 7人參與, 最新討論串1/1
開發平台(Platform): (Ex: VC++, GCC, Linux, ...) VC++ 2010 問題(Question): 事情是這樣的:(以下程式碼僅為片段,完整版在最後面) VectorCpx& VectorCpx::resize(unsigned place) { VectorCpx newVec(place, Complex(0.0,0.0)); delete[] pCpx; return newVec; } fstream file("outVec.txt", ios::out); int main() { VectorCpx v1; v1.resize(10); file << v1; } 有一個 class 名叫 VectorCpx,其中有一個成員函式 resize(unsigned place) VectorCpx 的功能是能夠創一個資料型態是 Complex(複數,另一個 class)的 vector 而 resize(unsigned place) 的功能則是改變已經創好的 vector,使它填入指定數目的0 (file 是自定義輸出) 這裡我的想法是直接創一個新的 VectorCpx,按照 resize(unsigned) 的要求填入0 把原本的 VectorCpx 砍掉,回傳新的回去 但實際上執行時,卻仍然是印出舊的 VectorCpx,而我自己測試確定新的有創造成功 請問我這種寫法哪裡有問題嗎?自己看是看不出個所以然來...... 感謝大家m(_ _)m 程式碼(Code):(請善用置底文網頁, 記得排版) VectorCpx.h:http://ideone.com/a8fcFl VectorCpx.cpp:http://ideone.com/u8voXB main.cpp:http://ideone.com/ZYi9CF 補充說明(Supplement): 這個是學校的功課,如果有需要的話我再把說明文件放上來0.0 -- 吾乃 不死之眾矢之的 無右之聯合之盾 武田軍最強騎兵團首席武將 不死鬼 馬場美濃守信房是也 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 220.133.35.4

04/13 14:28, , 1F
你弄錯 resize 要做什麼了: 他要你 resize 自己不是生個新的
04/13 14:28, 1F
我知道,不過我覺得直接弄一個新的丟回去比較簡單._.

04/13 16:26, , 2F
你newVec看起來沒有在任何地方指定給別人啊。
04/13 16:26, 2F

04/13 16:57, , 3F
你要丟回哪裡去? 使用者期待的是物件自己有變動啊
04/13 16:57, 3F
不太懂......0.0 我還是解釋一下resize的作用好了 resize(unsigned place, Complex cpx) 這個函式會傳入一個整數place,還有一個Complex cpx。 如果傳入的整數比vector的資料個數還大,那就把vector擴張到適合的大小,並且從原本 的資料之後一直到place的位置都填入cpx,如果當初沒有傳入cpx則填0 如果傳入的整數比vector裡面資料個數還少,那vector大小不變,但原本的資料從尾開始 砍到剩下符合的數量 例如說原本VectorCpx v1是[1][2][3][4][5][ ][ ][ ],資料個數是5,容量是8 此時v1.resize(10,6),因為10比5大,因此v1必須擴張到能夠大以塞入至少10筆資料 而原本的容量8不夠大,因此要擴張為16,之後v1裡要放入10筆資料 前面都是舊資料,後面一直到第10筆則全部填入"6" 因此變成[1][2][3][4][5][6][6][6][6][6][ ][ ][ ][ ][ ][ ],資料個數是10,容量是16 如果是v1.resize(7,6),雖然7比5大,但還在容量8範圍內,因此不用擴張陣列 變成[1][2][3][4][5][6][6][ ],資料個數是7,容量是8 如果是v1.resize(3,6),3比5小,因此不會塞入新的資料,反而舊資料要從尾砍到剩下3筆 容量不變,變成[1][2][3][ ][ ][ ][ ][ ] 至於resize(unsigned place)則是一種overload而已 我的想法是這樣: 與其要大費周章的去改變原本的v1的大小,把資料搬來搬去最後再回傳回去 倒不如直接創一個新的VectorCpx來實作這個功能 如果v1需要擴張的話,就創一個VectorCpx newVecBig(place,cpx) 這個vector的容量是我要的,資料數也是我要的,不一樣的是這個vector裡沒有舊資料 因此再把舊的資料一筆筆覆蓋到newVecBig相對應的位置上 如果v1不需要擴張,則創一個與v1相同大小的空VectorCpx newVecSmall 這個vector裡面沒有存任何的資料,因此把舊的資料一筆筆貼到相對應的位置 貼到我當初要求的資料數(place值)為止 最後再把v1 delete掉,回傳newVecBig或newVecSmall,這就是我要的vector 不過現在問題就出在不知道為什麼印出來的值仍然是v1的值...... 不知道我哪裡觀念錯誤0.0

04/13 17:20, , 4F
你回傳新東西是那招= =
04/13 17:20, 4F

04/13 18:30, , 5F
你傳回新的東西之後,你有把舊的記憶體釋放,然後
04/13 18:30, 5F

04/13 18:31, , 6F
回傳新的東西,但是你並沒有東西去接住
04/13 18:31, 6F

04/13 18:32, , 7F
原本的v1還是指向原本的v1,而不是你新回傳的
04/13 18:32, 7F

04/13 18:32, , 8F
你的解釋沒錯,但你複製之後應該要改掉原本的v1
04/13 18:32, 8F
喔喔大概開始有點概念了...... 不過不太知道要怎麼改,請問是用像是 this -> newVec之類的方法嗎? (不過編譯器不給我過就是,囧)

04/13 22:09, , 9F
需要求救阿......OAQ
04/13 22:09, 9F

04/13 22:12, , 10F
就用你原本覺得大費周章的方式做一遍吧。
04/13 22:12, 10F

04/13 22:19, , 11F
也不知道大費周章的方式怎麼做......只能想到這種方法
04/13 22:19, 11F

04/13 22:19, , 12F
而且沒有搞清楚之前總是覺得不甘心._.
04/13 22:19, 12F

04/13 22:22, , 13F
配置一塊新空間 搬移 釋放舊空間 將 pCpx 指向新空間
04/13 22:22, 13F

04/13 22:22, , 14F
更新 mSize, mCapacity
04/13 22:22, 14F
喔......還有這種方法,之前居然都沒想到,囧 感謝你的提醒...... 可是我還是好想知道我原來的想法觀念要怎麼實行阿 <囧>

04/13 22:57, , 15F
物件宣告完記憶體位址就是固定的。所以可以利用指標,
04/13 22:57, 15F

04/13 22:58, , 16F
隨時讓指標指向新的位址。如果有 N 個指標就要更新 N 次
04/13 22:58, 16F

04/13 22:59, , 17F
剛好你的物件裡有個 pCpx 可以讓你指,那顯然所有人都透過
04/13 22:59, 17F

04/13 23:00, , 18F
pCpx 是最方便的。
04/13 23:00, 18F
err......不好意思,我發現我不會指,囧 如果我沒理解錯你的意思的話,那應該是要把舊vector的每個空間的pCpx 指向新的vector的每個空間的pCpx指向的位址? 但是以我對這段話及對pointer觀念的理解,寫不出來囧...... for(unsigned int i = 0; i < mCapacity; i++) *pCpx[i] = &newVec.pCpx[i]; 這是我目前想出來的鬼東西......

04/13 23:16, , 19F
原po的想法不是本來就應該要用 linotwo 的方式實現嗎
04/13 23:16, 19F

04/13 23:36, , 20F
因為我很固執,很想看到我原本的作法能成功......
04/13 23:36, 20F

04/13 23:37, , 21F
雖然知道linotwo的作法會更好......
04/13 23:37, 21F

04/13 23:47, , 22F
resize回傳根本就沒意義啊...XD
04/13 23:47, 22F

04/13 23:49, , 23F
0.0?
04/13 23:49, 23F

04/13 23:51, , 24F
你的程式不能跑
04/13 23:51, 24F

04/13 23:52, , 25F
抓那三個也一樣,說 DBL_MIN 沒定義
04/13 23:52, 25F
0.0? 如果不管我剛才加的那段程式碼的話(那段是錯的),應該是可以跑才對阿...... 只是他cmd視窗不會有東西,會另外開一個.txt檔印出結果

04/13 23:55, , 26F
你印出的txt也沒東西= =
04/13 23:55, 26F

04/13 23:55, , 27F
DBL_MIN在float.h
04/13 23:55, 27F
我自己建專案跑的時候,至少確定可以印出A、B兩段阿0.0 而且也沒有跳警告說DBL_MIN找不到......奇怪

04/13 23:58, , 28F
反正你的問題就是你resize回傳的newVec沒有去用v1接
04/13 23:58, 28F

04/13 23:58, , 29F
你把main的v1.resize(10);改成v1=v1.resize(10);
04/13 23:58, 29F
結果程式當掉了......

04/14 00:04, , 30F
會當掉是因為resize時已經delete pCpx
04/14 00:04, 30F

04/14 00:06, , 31F
你resize回傳的時候,newVec就會被回收掉了吧
04/14 00:06, 31F
我還以為我delete的是原本v1的pCpx?!

04/14 00:07, , 32F
我跑程式不會當掉耶...
04/14 00:07, 32F

04/14 00:07, , 33F
O囗Q
04/14 00:07, 33F

04/14 00:21, , 34F
你改成用指標傳就可以
04/14 00:21, 34F

04/14 00:21, , 35F
跟那個 delete[] pCpx; 無關
04/14 00:21, 35F

04/14 00:23, , 36F
http://ideone.com/oCGYH3 總共改了四個地方
04/14 00:23, 36F

04/14 00:24, , 37F
後面加上 //<----this
04/14 00:24, 37F
error C2679: 二元運算子 '=' : 找不到使用右方運算元型別 'VectorCpx *' 的運算子 (或是沒有可接受的轉換) 所以說我連operator=的重載函式都寫錯了嗎......

04/14 01:05, , 38F
我是用你給的程式,改過四個地方而已
04/14 01:05, 38F

04/14 01:06, , 39F
還有把你 file << v1 << v2 << v3; 之後的都刪掉
04/14 01:06, 39F

04/14 01:12, , 40F
怎麼會這樣......
04/14 01:12, 40F

04/14 01:19, , 41F
總之觀念有錯...
04/14 01:19, 41F

04/14 01:21, , 42F
假設物件是房子, pCpx是屋內的家庭成員, resize時
04/14 01:21, 42F

04/14 01:23, , 43F
又蓋了一棟房子newVec,裡面的成員是複製原始v1的成員,
04/14 01:23, 43F

04/14 01:25, , 44F
最後又把v1房子內的成員清空, 因此v1不存在合法成員
04/14 01:25, 44F

04/14 01:28, , 45F
resize這個成員函式做的事情要很明確只有重新配置pCpx
04/14 01:28, 45F

04/14 01:29, , 46F
你必須要在這個function內完成這個工作,
04/14 01:29, 46F

04/14 01:35, , 47F
若要不修改原始code, 只需要在delete[] pCpx後加上
04/14 01:35, 47F

04/14 01:36, , 48F
memcpy(this, &newVec , sizeof(VectorCpx));
04/14 01:36, 48F

04/14 01:36, , 49F
newVec.pCpx = NULL;
04/14 01:36, 49F

04/14 01:41, , 50F
這樣就可以達到你的目的...
04/14 01:41, 50F

04/14 01:43, , 51F
你一開始問到為何印出仍是舊的,答案是delete[] pCpx時,
04/14 01:43, 51F

04/14 01:43, , 52F
pCpx指向的記憶體位置資料尚未被改變,
04/14 01:43, 52F

04/14 01:46, , 53F
請問如果他這樣改了,但是回傳的是new的位址,
04/14 01:46, 53F

04/14 01:47, , 54F
但是他在離開resize的時候就會被回收掉了,這樣錯吧
04/14 01:47, 54F

04/14 01:47, , 55F
所以印出的結果會是一樣,但這卻是不合法的取值,
04/14 01:47, 55F

04/14 01:49, , 56F
一般會建議用SAFE_DELETE的方式刪除(google有詳細)
04/14 01:49, 56F

04/14 01:52, , 57F
newVec在return時的確會被delete,但我在return之前將
04/14 01:52, 57F

04/14 01:52, , 58F
newVec.pCpx指向非法位置
04/14 01:52, 58F

04/14 01:53, , 59F
這樣就可以將newVec.pCpx存留在v1.pCpx
04/14 01:53, 59F

04/14 01:56, , 60F
所以這也就是存取已經被刪除的變數,什麼時候會爆炸
04/14 01:56, 60F

04/14 01:56, , 61F
都有可能摟? 那如果照我上面那樣改成指標可以嗎?
04/14 01:56, 61F

04/14 01:56, , 62F
改成指標去回傳,並且存取
04/14 01:56, 62F
這裡我想搞清楚一下0.0 所以rep大修補我的程式的方法是: 因為原本我的程式碼在delete[] pCpx之後,pCpx指向的記憶體位置裡的資料仍然沒變 (Q1:那請問我delete掉的是什麼?) 因此利用這段程式碼 memcpy(this, &newVec , sizeof(VectorCpx)); newVec.pCpx = NULL; (Q2-a:我有上網查過memcpy的用法和解釋,但還是不懂為什麼可以用在這邊......) (Q2-b:為什麼要把newVec.pCpx指向NULL?) 來把newVec.pCpx指向非法位址 (Q3:為什麼指向NULL是非法位址?因為他照理來說會在return時被delete掉嗎?) 就可以把newVec.pCpx留在v1.pCpx,以避免return時newVec被delete掉 (Q4:為什麼newVec在return時會被delete掉?) 大概是以上幾個問題還沒搞懂......

04/14 12:20, , 63F
感覺有個觀念要再提一次 (雖然 rep 在上面某處提過了)
04/14 12:20, 63F

04/14 12:21, , 64F
resize 不需要回傳東西, 使用者期望的功能是物件自己有變動
04/14 12:21, 64F

04/14 12:21, , 65F
所以原 PO 你在 resize 裡寫 return 就是這個觀念錯了而已
04/14 12:21, 65F

04/14 12:23, , 66F
所以討論到底 newVec 要給誰接個人覺得有點怪怪的就是
04/14 12:23, 66F

04/14 12:24, , 67F
那後來 rep 在提的方法其實就只是 swap 的概念而已
04/14 12:24, 67F

04/14 12:25, , 68F
把自己跟 newVec 的內容物進行交換 之後 newVec 就能扔了
04/14 12:25, 68F
感謝各位不厭其煩一直替我解惑......m(_ _)m 目前程式還是先依照我原本的作法,在rep大提供了那兩行程式碼後已經完成了99.9% 剩下的0.1%是個main裡奇怪的小問題: v3.push_back(Complex(0.5566, 0.9527)).push_back(Complex(0.2, 0.3)); 這樣會出問題,似乎是讀不到後面的push_back v3.push_back(Complex(0.5566, 0.9527)); v3.push_back(Complex(0.2, 0.3)); 這樣就對了 而push_back的程式碼內容都沒變,不知道為什麼...... 除此之外,這個程式基本上已經完成了 等段考完後,我會再用原本應該要用的正確觀念─使用者期望物件自己的變動 來把這個程式重寫一遍,不過這次也是學到了很多東西 現在希望先能把rep大講的那些東西好好搞懂...... (問題在上面0.0) 再次感謝大家的不辭勞苦 m(_ _)m

04/14 15:02, , 69F
等那些問題搞懂,寫好註解之後,我再把目前最終版程式碼
04/14 15:02, 69F

04/14 15:02, , 70F
貼上來0.0
04/14 15:02, 70F

04/14 22:54, , 71F
請問有人能幫我解惑嗎......0.0?
04/14 22:54, 71F

04/15 00:18, , 72F
delete只是告訴OS這個空間不再使用 並不會清掉空間裡的資料
04/15 00:18, 72F

04/15 00:37, , 73F
memcpy幫你把newVec的資料複製到你目前的這個物件
04/15 00:37, 73F

04/15 00:39, , 74F
把指標指向NULL只是讓你不小心存取到這個指標時看得出錯誤
04/15 00:39, 74F

04/15 00:40, , 75F
提取NULL指標的內容會產生runtime error 讓你看得出錯誤
04/15 00:40, 75F

04/15 00:41, , 76F
newVec是local variable所以離開function後就會消失
04/15 00:41, 76F

04/15 00:43, , 77F
return一個local variable的reference不合法的
04/15 00:43, 77F

04/15 00:44, , 78F
感覺你似乎不清楚變數的生命週期 先把這部分搞懂吧
04/15 00:44, 78F
總之我這次捅了個麻煩的大洞......0.0,等考完我再把這部份好好的磨助教磨到懂 以下是目前的最終版本 VectorCpx.h:http://ideone.com/qT1F8O VectorCpx.cpp:http://ideone.com/J00SSs main.cpp:http://ideone.com/awxpCS 有大家的幫忙,才能完成這份功課 最後希望我明後天的期中考能順利過關>"< ※ 編輯: o07608 來自: 220.133.35.4 (04/15 18:39)

04/15 18:41, , 79F
話說codepad最近怎麼都連不上去......
04/15 18:41, 79F
文章代碼(AID): #1HQFHWZt (C_and_CPP)