| 5 | 1/1 | 返回列表 |
| 查看: 1922 | 回復(fù): 6 | |||
| 當(dāng)前只顯示滿足指定條件的回帖,點(diǎn)擊這里查看本話題的所有回帖 | |||
[求助]
VC++中函數(shù)返回?cái)?shù)組指針或者帶指針的結(jié)構(gòu)體的編譯方式是否可?
|
|||
|
偶以前經(jīng)常采用這種方式來(lái)進(jìn)行C++的編程,只是在用delete釋放內(nèi)存時(shí)需要小心一些。 用這種方式編程的一個(gè)好處在于,比如計(jì)算矩陣運(yùn)算,例如A+B時(shí),可采用運(yùn)算符+的重載來(lái)進(jìn)行,這樣在使用程序包時(shí)會(huì)很方便。 可最近喜歡在結(jié)構(gòu)體中的使用析構(gòu)函數(shù),發(fā)現(xiàn)有個(gè)問(wèn)題,就是子函數(shù)返回結(jié)構(gòu)體時(shí),會(huì)自動(dòng)執(zhí)行析構(gòu)函數(shù)(即使當(dāng)此結(jié)構(gòu)體為外部變量時(shí)依然如此),因此把結(jié)構(gòu)體中的指針內(nèi)存提早釋放,導(dǎo)致返回的結(jié)構(gòu)體中的指針也相應(yīng)被釋放,最終出錯(cuò)。 我看書中的程序范例在計(jì)算矩陣或者多項(xiàng)式時(shí),都不是采用運(yùn)算符重載,而是用常規(guī)的函數(shù),把所用用到的結(jié)構(gòu)體都用加&的方式賦過(guò)來(lái),才不會(huì)被析構(gòu)函數(shù)釋放,可這種操作顯然太麻煩?但不知道是不是以前我的編程方式,本身就是一種錯(cuò)誤的習(xí)慣,還是說(shuō)干脆放棄使用析構(gòu)函數(shù),每次手動(dòng)釋放內(nèi)存?可是有沒(méi)有更好的方式可以二者兼得呢? |
|
matrix operator+(matrix& A) //矩陣加法 { int i,j; ANSm.build(m,n); if(n!=A.n||m!=A.m) { printf("\nERROR!!!\n行列不對(duì)應(yīng)的兩個(gè)矩陣不能相加。" ;return ANSm; } for(i=0;i return ANSm; } 這是定義在結(jié)構(gòu)體matrix中的一個(gè)成員,用于計(jì)算矩陣加法,其中m,n分別表示矩陣的行與列,二重指針M對(duì)應(yīng)矩陣的二維數(shù)組,對(duì)象ANSm是在外部定義的,在返回ANSm后,它就自動(dòng)執(zhí)行了ANSm中的析構(gòu)函數(shù),因此把返回結(jié)構(gòu)體中的指針也釋放了。 目前偶的一個(gè)辦法就是在結(jié)構(gòu)體中增加一個(gè)判定變量,使得析構(gòu)函數(shù)在主函數(shù)main結(jié)束前,不能釋放ANSm中的指針。 |
至尊木蟲 (著名寫手)
驃騎將軍
|
構(gòu)造函數(shù)+析構(gòu)函數(shù)應(yīng)該是比較正規(guī)的做法吧, 你的結(jié)構(gòu)體/類被提早釋放的問(wèn)題,是不是那個(gè)結(jié)構(gòu)體是局部變量,當(dāng)超出作用域時(shí)對(duì)象被銷毀,如果存在析構(gòu)函數(shù),會(huì)被默認(rèn)調(diào)用, 如果你返回結(jié)構(gòu)體/類的指針,按理不會(huì)被自動(dòng)析構(gòu)的 你最好貼一段出問(wèn)題的代碼來(lái)看看 |

木蟲 (著名寫手)
木蟲 (著名寫手)
|
條款23: 必須返回一個(gè)對(duì)象時(shí)不要試圖返回一個(gè)引用 據(jù)說(shuō)愛(ài)因斯坦曾提出過(guò)這樣的建議:盡可能地讓事情簡(jiǎn)單,但不要過(guò)于簡(jiǎn) 單。在C++語(yǔ)言中相似的說(shuō)法應(yīng)該是:盡可能地使程序高效,但不要過(guò)于高效。 一旦程序員抓住了“傳值”在效率上的把柄(參見條款22),他們會(huì)變得 十分極端,恨不得挖出每一個(gè)隱藏在程序中的傳值操作。豈不知,在他們不懈 地追求純粹的“傳引用”的過(guò)程中,他們會(huì)不可避免地犯另一個(gè)嚴(yán)重的錯(cuò)誤: 傳遞一個(gè)并不存在的對(duì)象的引用。這就不是好事了。 看一個(gè)表示有理數(shù)的類,其中包含一個(gè)友元函數(shù),用于兩個(gè)有理數(shù)相乘: class Rational { public: Rational(int numerator = 0, int denominator = 1); ... private: int n, d; // 分子和分母 friend const Rational // 參見條款21:為什么 operator*(const Rational& lhs, // 返回值是const const Rational& rhs) }; inline const Rational operator*(const Rational& lhs, const Rational& rhs) { return Rational(lhs.n * rhs.n, lhs.d * rhs.d); } 很明顯,這個(gè)版本的operator*是通過(guò)傳值返回對(duì)象結(jié)果,如果不去考慮對(duì) 象構(gòu)造和析構(gòu)時(shí)的開銷,你就是在逃避作為一個(gè)程序員的責(zé)任。另外一件很明 顯的事實(shí)是,除非確實(shí)有必要,否則誰(shuí)都不愿意承擔(dān)這樣一個(gè)臨時(shí)對(duì)象的開銷。 那么,問(wèn)題就歸結(jié)于:確實(shí)有必要嗎? 答案是,如果能返回一個(gè)引用,當(dāng)然就沒(méi)有必要。但請(qǐng)記住,引用只是一 個(gè)名字,一個(gè)其它某個(gè)已經(jīng)存在的對(duì)象的名字。無(wú)論何時(shí)看到一個(gè)引用的聲明, 就要立即問(wèn)自己:它的另一個(gè)名字是什么呢?因?yàn)樗厝贿有另外一個(gè)什么名 字(見條款M1)。拿operator*來(lái)說(shuō),如果函數(shù)要返回一個(gè)引用,那它返回的必 須是其它某個(gè)已經(jīng)存在的Rational 對(duì)象的引用,這個(gè)對(duì)象包含了兩個(gè)對(duì)象相乘 的結(jié)果。 但,期望在調(diào)用 operator*之前有這樣一個(gè)對(duì)象存在是沒(méi)道理的。也就是說(shuō), 如果有下面的代碼: Rational a(1, 2); // a = 1/2 Rational b(3, 5); // b = 3/5 Rational c = a * b; // c 為 3/10 期望已經(jīng)存在一個(gè)值為 3/10 的有理數(shù)是不現(xiàn)實(shí)的。如果operator* 一定要 返回這樣一個(gè)數(shù)的引用,就必須自己創(chuàng)建這個(gè)數(shù)的對(duì)象。 一個(gè)函數(shù)只能有兩種方法創(chuàng)建一個(gè)新對(duì)象:在堆棧里或在堆上。在堆棧里 創(chuàng)建對(duì)象時(shí)伴隨著一個(gè)局部變量的定義,采用這種方法,就要這樣寫operator*: // 寫此函數(shù)的第一個(gè)錯(cuò)誤方法 inline const Rational& operator*(const Rational& lhs, const Rational& rhs) { Rational result(lhs.n * rhs.n, lhs.d * rhs.d); return result; } 這個(gè)方法應(yīng)該被否決,因?yàn)槲覀兊哪繕?biāo)是避免構(gòu)造函數(shù)被調(diào)用,但 result 必須要象其它對(duì)象一樣被構(gòu)造。另外,這個(gè)函數(shù)還有另外一個(gè)更嚴(yán)重的問(wèn)題, 它返回的是一個(gè)局部對(duì)象的引用,關(guān)于這個(gè)錯(cuò)誤,條款31 進(jìn)行了深入的討論。 那么,在堆上創(chuàng)建一個(gè)對(duì)象然后返回它的引用呢?基于堆的對(duì)象是通過(guò)使 用new 產(chǎn)生的,所以應(yīng)該這樣寫operator*: // 寫此函數(shù)的第二個(gè)錯(cuò)誤方法 inline const Rational& operator*(const Rational& lhs, const Rational& rhs) { Rational *result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d); return *result; } 首先,你還是得負(fù)擔(dān)構(gòu)造函數(shù)調(diào)用的開銷,因?yàn)?new 分配的內(nèi)存是通過(guò)調(diào) 用一個(gè)適當(dāng)?shù)臉?gòu)造函數(shù)來(lái)初始化的(見條款5 和M8)。另外,還有一個(gè)問(wèn)題: 誰(shuí)將負(fù)責(zé)用delete 來(lái)刪除掉new 生成的對(duì)象呢? 實(shí)際上,這絕對(duì)是一個(gè)內(nèi)存泄漏。即使可以說(shuō)服 operator*的調(diào)用者去取函 數(shù)返回值地址,然后用delete 去刪除它(絕對(duì)不可能——條款31 展示了這樣 的代碼會(huì)是什么樣的),但一些復(fù)雜的表達(dá)式會(huì)產(chǎn)生沒(méi)有名字的臨時(shí)值,程序員 是不可能得到的。例如: Rational w, x, y, z; w = x * y * z; 兩個(gè)對(duì) operator*的調(diào)用都產(chǎn)生了沒(méi)有名字的臨時(shí)值,程序員無(wú)法看到,因 而無(wú)法刪除。(再次參見條款31) 也許,你會(huì)想你比一般的熊——或一般的程序員——要聰明;也許,你注 意到在堆棧和堆上創(chuàng)建對(duì)象的方法避免不了對(duì)構(gòu)造函數(shù)的調(diào)用;也許,你想起 了我們最初的目標(biāo)是為了避免這種對(duì)構(gòu)造函數(shù)的調(diào)用;也許,你有個(gè)辦法可以 只用一個(gè)構(gòu)造函數(shù)來(lái)搞掂一切;也許,你的眼前出現(xiàn)了這樣一段代碼:operator* 返回一個(gè)“在函數(shù)內(nèi)部定義的靜態(tài)Rational 對(duì)象”的引用: // 寫此函數(shù)的第三個(gè)錯(cuò)誤方法 inline const Rational& operator*(const Rational& lhs, const Rational& rhs) { static Rational result; // 將要作為引用返回的 // 靜態(tài)對(duì)象 lhs 和rhs 相乘,結(jié)果放進(jìn)result; return result; } 這個(gè)方法看起來(lái)好象有戲,雖然在實(shí)際實(shí)現(xiàn)上面的偽代碼時(shí)你會(huì)發(fā)現(xiàn),不 調(diào)用一個(gè)Rational 構(gòu)造函數(shù)是不可能給出result 的正確值的,而避免這樣的調(diào) 用正是我們要談?wù)摰闹黝}。就算你實(shí)現(xiàn)了上面的偽代碼,但,你再聰明也不能 最終挽救這個(gè)不幸的設(shè)計(jì)。 想知道為什么,看看下面這段寫得很合理的用戶代碼: bool operator==(const Rational& lhs, // Rationals 的operator== const Rational& rhs); // Rational a, b, c, d; ... if ((a * b) == (c * d)) { 處理相等的情況; } else { 處理不相等的情況; } 看出來(lái)了嗎?((a*b) == (c*d)) 會(huì)永遠(yuǎn)為true,不管a,b,c 和d 是什么值! 用等價(jià)的函數(shù)形式重寫上面的相等判斷語(yǔ)句就很容易明白發(fā)生這一可惡行 為的原因了: if (operator==(operator*(a, b), operator*(c, d))) 注意當(dāng) operator==被調(diào)用時(shí),總有兩個(gè)operator*剛被調(diào)用,每個(gè)調(diào)用返回 operator*內(nèi)部的靜態(tài)Rational 對(duì)象的引用。于是,上面的語(yǔ)句實(shí)際上是請(qǐng)求 operator==對(duì)“operator*內(nèi)部的靜態(tài)Rational 對(duì)象的值”和“operator*內(nèi)部的 靜態(tài)Rational 對(duì)象的值”進(jìn)行比較,這樣的比較不相等才怪呢! 幸運(yùn)的話,我以上的說(shuō)明應(yīng)該足以說(shuō)服你:想“在象operator*這樣的函數(shù) 里返回一個(gè)引用”實(shí)際上是在浪費(fèi)時(shí)間。但我沒(méi)幼稚到會(huì)相信幸運(yùn)總會(huì)光臨自 己。一些人——你們知道這些人是指誰(shuí)——此刻會(huì)在想,“唔,上面那個(gè)方法, 如果一個(gè)靜態(tài)變量不夠用,也許可以用一個(gè)靜態(tài)數(shù)組⋯⋯” 請(qǐng)就此打!我們難道還沒(méi)受夠嗎? 我不能讓自己寫一段示例代碼來(lái)太高這個(gè)設(shè)計(jì),因?yàn)榧词怪槐в猩厦孢@種 想法都足以令人感到羞愧。首先,你必須選擇一個(gè)n,指定數(shù)組的大小。如果 n 太小,就會(huì)沒(méi)地方儲(chǔ)存函數(shù)返回值,這和我們前面否定的那個(gè)“采用單個(gè)靜 態(tài)變量的設(shè)計(jì)”相比沒(méi)有什么改進(jìn)。如果n 太大,就會(huì)降低程序的性能,因?yàn)?br /> 函數(shù)第一次被調(diào)用時(shí)數(shù)組中每個(gè)對(duì)象都要被創(chuàng)建。這會(huì)帶來(lái)n 個(gè)構(gòu)造函數(shù)和n 個(gè)析構(gòu)函數(shù)的開銷,即使這個(gè)函數(shù)只被調(diào)用一次。如果說(shuō)"optimization"(最優(yōu) 化) 是指提高軟件的性能的過(guò)程, 那 么 現(xiàn) 在 這 種 做 法 簡(jiǎn) 直 可 以 稱 為 "pessimization"(最差化)。最后,想想怎么把需要的值放到數(shù)組的對(duì)象中以及 需要多大的開銷?在對(duì)象間傳值的最直接的方法是通過(guò)賦值,但賦值的開銷又 有多大呢?一般來(lái)說(shuō),它相當(dāng)于調(diào)用一個(gè)析構(gòu)函數(shù)(摧毀舊值)再加上調(diào)用一 個(gè)構(gòu)造函數(shù)(拷貝新值)。但我們現(xiàn)在的目標(biāo)正是為了避免構(gòu)造和析構(gòu)的開銷! 面對(duì)現(xiàn)實(shí)吧:這個(gè)方法也絕對(duì)不能選用。 所以,寫一個(gè)必須返回一個(gè)新對(duì)象的函數(shù)的正確方法就是讓這個(gè)函數(shù)返回 一個(gè)新對(duì)象。對(duì)于Rational 的operator*來(lái)說(shuō),這意味著要不就是下面的代碼(就 是最初看到的那段代碼),要不就是本質(zhì)上和它等價(jià)的代碼: inline const Rational operator*(const Rational& lhs, const Rational& rhs) { return Rational(lhs.n * rhs.n, lhs.d * rhs.d); } 的確,這會(huì)導(dǎo)致“operator*的返回值構(gòu)造和析構(gòu)時(shí)帶來(lái)的開銷”,但歸根 結(jié)底它只是用小的代價(jià)換來(lái)正確的程序運(yùn)行行為而已。況且,你所擔(dān)心的開銷 還有可能永遠(yuǎn)不會(huì)出現(xiàn):和所有程序設(shè)計(jì)語(yǔ)言一樣,C++允許編譯器的設(shè)計(jì)者 采用一些優(yōu)化措施來(lái)提高所生成的代碼的性能,所以,在有些場(chǎng)合,operator* 的返回值會(huì)被安全地除去(見條款M20)。當(dāng)編譯器采用了這種優(yōu)化時(shí)(當(dāng)前 大部分編譯器這么做),程序和以前一樣繼續(xù)工作,只不過(guò)是運(yùn)行速度比你預(yù)計(jì) 的要快而已。 以上討論可以歸結(jié)為:當(dāng)需要在返回引用和返回對(duì)象間做決定時(shí),你的職 責(zé)是選擇可以完成正確功能的那個(gè)。至于怎么讓這個(gè)選擇所產(chǎn)生的代價(jià)盡可能 的小,那是編譯器的生產(chǎn)商去想的事。 |
| 最具人氣熱帖推薦 [查看全部] | 作者 | 回/看 | 最后發(fā)表 | |
|---|---|---|---|---|
|
[考研] 085601材料工程找調(diào)劑 +14 | oatmealR 2026-03-29 | 15/750 |
|
|---|---|---|---|---|
|
[考博] 26申博自薦 +6 | whh869393 2026-03-24 | 6/300 |
|
|
[考研] 材料與化工328分調(diào)劑 +7 | 。,。,。,。i 2026-03-23 | 7/350 |
|
|
[考研] 材料專碩調(diào)劑 +5 | 椰椰。 2026-03-29 | 5/250 |
|
|
[考研] 291求調(diào)劑 +7 | Y-cap 2026-03-29 | 8/400 |
|
|
[考研] 調(diào)劑考研 +3 | 王杰一 2026-03-29 | 3/150 |
|
|
[考研] 330分求調(diào)劑 +5 | qzenlc 2026-03-29 | 5/250 |
|
|
[考研] 調(diào)劑 +3 | 好好讀書。 2026-03-28 | 3/150 |
|
|
[考研] 339求調(diào)劑,想調(diào)回江蘇 +6 | 烤麥芽 2026-03-27 | 8/400 |
|
|
[考研] 085405 考的11408求各位老師帶走 +3 | Qiu學(xué)ing 2026-03-28 | 3/150 |
|
|
[考研] 291求調(diào)劑 +6 | HanBeiNingZC 2026-03-24 | 6/300 |
|
|
[考研] 求調(diào)劑推薦 材料 304 +15 | 荷包蛋hyj 2026-03-26 | 15/750 |
|
|
[考研] 求調(diào)劑 +4 | 零八# 2026-03-27 | 4/200 |
|
|
[考研]
|
18419759900 2026-03-25 | 8/400 |
|
|
[考研] 材料292調(diào)劑 +12 | 橘頌思美人 2026-03-23 | 12/600 |
|
|
[考研] 322求調(diào)劑 +4 | 我真的很想學(xué)習(xí) 2026-03-23 | 4/200 |
|
|
[考研] 網(wǎng)絡(luò)空間安全0839招調(diào)劑 +4 | w320357296 2026-03-25 | 6/300 |
|
|
[考研] 材料專碩找調(diào)劑 +5 | 哈哈哈吼吼吼哈 2026-03-23 | 5/250 |
|
|
[考研] 277分求調(diào)劑,跨調(diào)材料 +3 | 考研調(diào)劑lxh 2026-03-24 | 3/150 |
|
|
[考研] 336化工調(diào)劑 +4 | 王大坦1 2026-03-23 | 5/250 |
|