| 7 | 1/1 | 返回列表 |
| 查看: 1905 | 回復(fù): 6 | ||
[求助]
VC++中函數(shù)返回數(shù)組指針或者帶指針的結(jié)構(gòu)體的編譯方式是否可。
|
|
偶以前經(jīng)常采用這種方式來進行C++的編程,只是在用delete釋放內(nèi)存時需要小心一些。 用這種方式編程的一個好處在于,比如計算矩陣運算,例如A+B時,可采用運算符+的重載來進行,這樣在使用程序包時會很方便。 可最近喜歡在結(jié)構(gòu)體中的使用析構(gòu)函數(shù),發(fā)現(xiàn)有個問題,就是子函數(shù)返回結(jié)構(gòu)體時,會自動執(zhí)行析構(gòu)函數(shù)(即使當(dāng)此結(jié)構(gòu)體為外部變量時依然如此),因此把結(jié)構(gòu)體中的指針內(nèi)存提早釋放,導(dǎo)致返回的結(jié)構(gòu)體中的指針也相應(yīng)被釋放,最終出錯。 我看書中的程序范例在計算矩陣或者多項式時,都不是采用運算符重載,而是用常規(guī)的函數(shù),把所用用到的結(jié)構(gòu)體都用加&的方式賦過來,才不會被析構(gòu)函數(shù)釋放,可這種操作顯然太麻煩?但不知道是不是以前我的編程方式,本身就是一種錯誤的習(xí)慣,還是說干脆放棄使用析構(gòu)函數(shù),每次手動釋放內(nèi)存?可是有沒有更好的方式可以二者兼得呢? |
至尊木蟲 (著名寫手)
驃騎將軍

木蟲 (著名寫手)
|
matrix operator+(matrix& A) //矩陣加法 { int i,j; ANSm.build(m,n); if(n!=A.n||m!=A.m) { printf("\nERROR!!!\n行列不對應(yīng)的兩個矩陣不能相加。" ;return ANSm; } for(i=0;i return ANSm; } 這是定義在結(jié)構(gòu)體matrix中的一個成員,用于計算矩陣加法,其中m,n分別表示矩陣的行與列,二重指針M對應(yīng)矩陣的二維數(shù)組,對象ANSm是在外部定義的,在返回ANSm后,它就自動執(zhí)行了ANSm中的析構(gòu)函數(shù),因此把返回結(jié)構(gòu)體中的指針也釋放了。 目前偶的一個辦法就是在結(jié)構(gòu)體中增加一個判定變量,使得析構(gòu)函數(shù)在主函數(shù)main結(jié)束前,不能釋放ANSm中的指針。 |
木蟲 (著名寫手)
|
條款23: 必須返回一個對象時不要試圖返回一個引用 據(jù)說愛因斯坦曾提出過這樣的建議:盡可能地讓事情簡單,但不要過于簡 單。在C++語言中相似的說法應(yīng)該是:盡可能地使程序高效,但不要過于高效。 一旦程序員抓住了“傳值”在效率上的把柄(參見條款22),他們會變得 十分極端,恨不得挖出每一個隱藏在程序中的傳值操作。豈不知,在他們不懈 地追求純粹的“傳引用”的過程中,他們會不可避免地犯另一個嚴重的錯誤: 傳遞一個并不存在的對象的引用。這就不是好事了。 看一個表示有理數(shù)的類,其中包含一個友元函數(shù),用于兩個有理數(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); } 很明顯,這個版本的operator*是通過傳值返回對象結(jié)果,如果不去考慮對 象構(gòu)造和析構(gòu)時的開銷,你就是在逃避作為一個程序員的責(zé)任。另外一件很明 顯的事實是,除非確實有必要,否則誰都不愿意承擔(dān)這樣一個臨時對象的開銷。 那么,問題就歸結(jié)于:確實有必要嗎? 答案是,如果能返回一個引用,當(dāng)然就沒有必要。但請記住,引用只是一 個名字,一個其它某個已經(jīng)存在的對象的名字。無論何時看到一個引用的聲明, 就要立即問自己:它的另一個名字是什么呢?因為它必然還有另外一個什么名 字(見條款M1)。拿operator*來說,如果函數(shù)要返回一個引用,那它返回的必 須是其它某個已經(jīng)存在的Rational 對象的引用,這個對象包含了兩個對象相乘 的結(jié)果。 但,期望在調(diào)用 operator*之前有這樣一個對象存在是沒道理的。也就是說, 如果有下面的代碼: Rational a(1, 2); // a = 1/2 Rational b(3, 5); // b = 3/5 Rational c = a * b; // c 為 3/10 期望已經(jīng)存在一個值為 3/10 的有理數(shù)是不現(xiàn)實的。如果operator* 一定要 返回這樣一個數(shù)的引用,就必須自己創(chuàng)建這個數(shù)的對象。 一個函數(shù)只能有兩種方法創(chuàng)建一個新對象:在堆棧里或在堆上。在堆棧里 創(chuàng)建對象時伴隨著一個局部變量的定義,采用這種方法,就要這樣寫operator*: // 寫此函數(shù)的第一個錯誤方法 inline const Rational& operator*(const Rational& lhs, const Rational& rhs) { Rational result(lhs.n * rhs.n, lhs.d * rhs.d); return result; } 這個方法應(yīng)該被否決,因為我們的目標(biāo)是避免構(gòu)造函數(shù)被調(diào)用,但 result 必須要象其它對象一樣被構(gòu)造。另外,這個函數(shù)還有另外一個更嚴重的問題, 它返回的是一個局部對象的引用,關(guān)于這個錯誤,條款31 進行了深入的討論。 那么,在堆上創(chuàng)建一個對象然后返回它的引用呢?基于堆的對象是通過使 用new 產(chǎn)生的,所以應(yīng)該這樣寫operator*: // 寫此函數(shù)的第二個錯誤方法 inline const Rational& operator*(const Rational& lhs, const Rational& rhs) { Rational *result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d); return *result; } 首先,你還是得負擔(dān)構(gòu)造函數(shù)調(diào)用的開銷,因為 new 分配的內(nèi)存是通過調(diào) 用一個適當(dāng)?shù)臉?gòu)造函數(shù)來初始化的(見條款5 和M8)。另外,還有一個問題: 誰將負責(zé)用delete 來刪除掉new 生成的對象呢? 實際上,這絕對是一個內(nèi)存泄漏。即使可以說服 operator*的調(diào)用者去取函 數(shù)返回值地址,然后用delete 去刪除它(絕對不可能——條款31 展示了這樣 的代碼會是什么樣的),但一些復(fù)雜的表達式會產(chǎn)生沒有名字的臨時值,程序員 是不可能得到的。例如: Rational w, x, y, z; w = x * y * z; 兩個對 operator*的調(diào)用都產(chǎn)生了沒有名字的臨時值,程序員無法看到,因 而無法刪除。(再次參見條款31) 也許,你會想你比一般的熊——或一般的程序員——要聰明;也許,你注 意到在堆棧和堆上創(chuàng)建對象的方法避免不了對構(gòu)造函數(shù)的調(diào)用;也許,你想起 了我們最初的目標(biāo)是為了避免這種對構(gòu)造函數(shù)的調(diào)用;也許,你有個辦法可以 只用一個構(gòu)造函數(shù)來搞掂一切;也許,你的眼前出現(xiàn)了這樣一段代碼:operator* 返回一個“在函數(shù)內(nèi)部定義的靜態(tài)Rational 對象”的引用: // 寫此函數(shù)的第三個錯誤方法 inline const Rational& operator*(const Rational& lhs, const Rational& rhs) { static Rational result; // 將要作為引用返回的 // 靜態(tài)對象 lhs 和rhs 相乘,結(jié)果放進result; return result; } 這個方法看起來好象有戲,雖然在實際實現(xiàn)上面的偽代碼時你會發(fā)現(xiàn),不 調(diào)用一個Rational 構(gòu)造函數(shù)是不可能給出result 的正確值的,而避免這樣的調(diào) 用正是我們要談?wù)摰闹黝}。就算你實現(xiàn)了上面的偽代碼,但,你再聰明也不能 最終挽救這個不幸的設(shè)計。 想知道為什么,看看下面這段寫得很合理的用戶代碼: bool operator==(const Rational& lhs, // Rationals 的operator== const Rational& rhs); // Rational a, b, c, d; ... if ((a * b) == (c * d)) { 處理相等的情況; } else { 處理不相等的情況; } 看出來了嗎?((a*b) == (c*d)) 會永遠為true,不管a,b,c 和d 是什么值! 用等價的函數(shù)形式重寫上面的相等判斷語句就很容易明白發(fā)生這一可惡行 為的原因了: if (operator==(operator*(a, b), operator*(c, d))) 注意當(dāng) operator==被調(diào)用時,總有兩個operator*剛被調(diào)用,每個調(diào)用返回 operator*內(nèi)部的靜態(tài)Rational 對象的引用。于是,上面的語句實際上是請求 operator==對“operator*內(nèi)部的靜態(tài)Rational 對象的值”和“operator*內(nèi)部的 靜態(tài)Rational 對象的值”進行比較,這樣的比較不相等才怪呢! 幸運的話,我以上的說明應(yīng)該足以說服你:想“在象operator*這樣的函數(shù) 里返回一個引用”實際上是在浪費時間。但我沒幼稚到會相信幸運總會光臨自 己。一些人——你們知道這些人是指誰——此刻會在想,“唔,上面那個方法, 如果一個靜態(tài)變量不夠用,也許可以用一個靜態(tài)數(shù)組⋯⋯” 請就此打!我們難道還沒受夠嗎? 我不能讓自己寫一段示例代碼來太高這個設(shè)計,因為即使只抱有上面這種 想法都足以令人感到羞愧。首先,你必須選擇一個n,指定數(shù)組的大小。如果 n 太小,就會沒地方儲存函數(shù)返回值,這和我們前面否定的那個“采用單個靜 態(tài)變量的設(shè)計”相比沒有什么改進。如果n 太大,就會降低程序的性能,因為 函數(shù)第一次被調(diào)用時數(shù)組中每個對象都要被創(chuàng)建。這會帶來n 個構(gòu)造函數(shù)和n 個析構(gòu)函數(shù)的開銷,即使這個函數(shù)只被調(diào)用一次。如果說"optimization"(最優(yōu) 化) 是指提高軟件的性能的過程, 那 么 現(xiàn) 在 這 種 做 法 簡 直 可 以 稱 為 "pessimization"(最差化)。最后,想想怎么把需要的值放到數(shù)組的對象中以及 需要多大的開銷?在對象間傳值的最直接的方法是通過賦值,但賦值的開銷又 有多大呢?一般來說,它相當(dāng)于調(diào)用一個析構(gòu)函數(shù)(摧毀舊值)再加上調(diào)用一 個構(gòu)造函數(shù)(拷貝新值)。但我們現(xiàn)在的目標(biāo)正是為了避免構(gòu)造和析構(gòu)的開銷啊! 面對現(xiàn)實吧:這個方法也絕對不能選用。 所以,寫一個必須返回一個新對象的函數(shù)的正確方法就是讓這個函數(shù)返回 一個新對象。對于Rational 的operator*來說,這意味著要不就是下面的代碼(就 是最初看到的那段代碼),要不就是本質(zhì)上和它等價的代碼: inline const Rational operator*(const Rational& lhs, const Rational& rhs) { return Rational(lhs.n * rhs.n, lhs.d * rhs.d); } 的確,這會導(dǎo)致“operator*的返回值構(gòu)造和析構(gòu)時帶來的開銷”,但歸根 結(jié)底它只是用小的代價換來正確的程序運行行為而已。況且,你所擔(dān)心的開銷 還有可能永遠不會出現(xiàn):和所有程序設(shè)計語言一樣,C++允許編譯器的設(shè)計者 采用一些優(yōu)化措施來提高所生成的代碼的性能,所以,在有些場合,operator* 的返回值會被安全地除去(見條款M20)。當(dāng)編譯器采用了這種優(yōu)化時(當(dāng)前 大部分編譯器這么做),程序和以前一樣繼續(xù)工作,只不過是運行速度比你預(yù)計 的要快而已。 以上討論可以歸結(jié)為:當(dāng)需要在返回引用和返回對象間做決定時,你的職 責(zé)是選擇可以完成正確功能的那個。至于怎么讓這個選擇所產(chǎn)生的代價盡可能 的小,那是編譯器的生產(chǎn)商去想的事。 |
|
【VC++中函數(shù)返回數(shù)組指針或者帶指針的結(jié)構(gòu)體的編譯方式是否可取?】 題目上問的那兩個問題: 變量是具有生存周期的。在某個函數(shù)中聲明、且定義的變量(糾結(jié),這個問題讓我想起了extern中的declares but does not define,- -||||黑線),都會在函數(shù)return之后結(jié)束生存周期,無論那個變量是指針還是結(jié)構(gòu)體。 如果你的結(jié)構(gòu)體(class example)重載了‘=’,能夠賦值,那么函數(shù)返回值為普通結(jié)構(gòu)體,做法可行。 返回帶指針的結(jié)構(gòu)體,要防止指針指向的內(nèi)存,因結(jié)束生命周期而失效。(尤其是指向函數(shù)變量的,“過期就作廢”;但是用new, malloc開辟空間的不會)。 我主要是做圖像方面。會用到很多矩陣。圖像矩陣類cvMat操作的時候,基本上都是用指針。 cvMat* cvCreateMat ( int rows, int cols, int type ); 是建立矩陣。返回值是指針。 例如mat1 = cvCreateMat( 3, 4, sizeof(char) ) 無論執(zhí)行什么函數(shù),最通常的做法就是,把mat1指針各種傳遞。 |
鐵蟲 (初入文壇)
| 7 | 1/1 | 返回列表 |
| 最具人氣熱帖推薦 [查看全部] | 作者 | 回/看 | 最后發(fā)表 | |
|---|---|---|---|---|
|
[考研] 290求調(diào)劑 +4 | ^O^乜 2026-03-19 | 4/200 |
|
|---|---|---|---|---|
|
[考研] 工科材料085601 279求調(diào)劑 +6 | 困于星晨 2026-03-17 | 8/400 |
|
|
[考研] 298-一志愿中國農(nóng)業(yè)大學(xué)-求調(diào)劑 +8 | 手機用戶 2026-03-17 | 8/400 |
|
|
[考研]
|
.6lL 2026-03-18 | 7/350 |
|
|
[考研] 329求調(diào)劑 +6 | 想上學(xué)吖吖 2026-03-19 | 6/300 |
|
|
[考研] 一志愿中國海洋大學(xué),生物學(xué),301分,求調(diào)劑 +5 | 1孫悟空 2026-03-17 | 6/300 |
|
|
[考博] 申博26年 +3 | 八6八68 2026-03-19 | 3/150 |
|
|
[考博] 東華理工大學(xué)化材專業(yè)26屆碩士博士申請 +8 | zlingli 2026-03-13 | 8/400 |
|
|
[考研] 一志愿北京化工大學(xué)0703化學(xué)318分,有科研經(jīng)歷,求調(diào)劑 +3 | 一瓶苯甲酸 2026-03-14 | 3/150 |
|
|
[考研] 286求調(diào)劑 +6 | lemonzzn 2026-03-16 | 10/500 |
|
|
[考研] 344求調(diào)劑 +6 | knight344 2026-03-16 | 7/350 |
|
|
[考研] 08工科 320總分 求調(diào)劑 +5 | 梨花珞晚風(fēng) 2026-03-17 | 5/250 |
|
|
[考研] 304求調(diào)劑 +12 | 小熊joy 2026-03-14 | 13/650 |
|
|
[考研] 材料,紡織,生物(0856、0710),化學(xué)招生啦 +3 | Eember. 2026-03-17 | 9/450 |
|
|
[考研] 一志愿211 0703方向310分求調(diào)劑 +3 | 努力奮斗112 2026-03-15 | 3/150 |
|
|
[考研] 304求調(diào)劑 +3 | 曼殊2266 2026-03-14 | 3/150 |
|
|
[考研] 0856求調(diào)劑 +3 | 劉夢微 2026-03-15 | 3/150 |
|
|
[考研] 326求調(diào)劑 +3 | mlpqaz03 2026-03-15 | 3/150 |
|
|
[考研] 085601材料工程315分求調(diào)劑 +3 | yang_0104 2026-03-15 | 3/150 |
|
|
[考研] 266求調(diào)劑 +4 | 學(xué)員97LZgn 2026-03-13 | 4/200 |
|