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

|
matrix operator+(matrix& A) //矩陣加法 { int i,j; ANSm.build(m,n); if(n!=A.n||m!=A.m) { printf("\nERROR!!!\n行列不對應的兩個矩陣不能相加。" ;return ANSm; } for(i=0;i return ANSm; } 這是定義在結構體matrix中的一個成員,用于計算矩陣加法,其中m,n分別表示矩陣的行與列,二重指針M對應矩陣的二維數(shù)組,對象ANSm是在外部定義的,在返回ANSm后,它就自動執(zhí)行了ANSm中的析構函數(shù),因此把返回結構體中的指針也釋放了。 目前偶的一個辦法就是在結構體中增加一個判定變量,使得析構函數(shù)在主函數(shù)main結束前,不能釋放ANSm中的指針。 |
木蟲 (著名寫手)
|
條款23: 必須返回一個對象時不要試圖返回一個引用 據(jù)說愛因斯坦曾提出過這樣的建議:盡可能地讓事情簡單,但不要過于簡 單。在C++語言中相似的說法應該是:盡可能地使程序高效,但不要過于高效。 一旦程序員抓住了“傳值”在效率上的把柄(參見條款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*是通過傳值返回對象結果,如果不去考慮對 象構造和析構時的開銷,你就是在逃避作為一個程序員的責任。另外一件很明 顯的事實是,除非確實有必要,否則誰都不愿意承擔這樣一個臨時對象的開銷。 那么,問題就歸結于:確實有必要嗎? 答案是,如果能返回一個引用,當然就沒有必要。但請記住,引用只是一 個名字,一個其它某個已經存在的對象的名字。無論何時看到一個引用的聲明, 就要立即問自己:它的另一個名字是什么呢?因為它必然還有另外一個什么名 字(見條款M1)。拿operator*來說,如果函數(shù)要返回一個引用,那它返回的必 須是其它某個已經存在的Rational 對象的引用,這個對象包含了兩個對象相乘 的結果。 但,期望在調用 operator*之前有這樣一個對象存在是沒道理的。也就是說, 如果有下面的代碼: Rational a(1, 2); // a = 1/2 Rational b(3, 5); // b = 3/5 Rational c = a * b; // c 為 3/10 期望已經存在一個值為 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; } 這個方法應該被否決,因為我們的目標是避免構造函數(shù)被調用,但 result 必須要象其它對象一樣被構造。另外,這個函數(shù)還有另外一個更嚴重的問題, 它返回的是一個局部對象的引用,關于這個錯誤,條款31 進行了深入的討論。 那么,在堆上創(chuàng)建一個對象然后返回它的引用呢?基于堆的對象是通過使 用new 產生的,所以應該這樣寫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; } 首先,你還是得負擔構造函數(shù)調用的開銷,因為 new 分配的內存是通過調 用一個適當?shù)臉嬙旌瘮?shù)來初始化的(見條款5 和M8)。另外,還有一個問題: 誰將負責用delete 來刪除掉new 生成的對象呢? 實際上,這絕對是一個內存泄漏。即使可以說服 operator*的調用者去取函 數(shù)返回值地址,然后用delete 去刪除它(絕對不可能——條款31 展示了這樣 的代碼會是什么樣的),但一些復雜的表達式會產生沒有名字的臨時值,程序員 是不可能得到的。例如: Rational w, x, y, z; w = x * y * z; 兩個對 operator*的調用都產生了沒有名字的臨時值,程序員無法看到,因 而無法刪除。(再次參見條款31) 也許,你會想你比一般的熊——或一般的程序員——要聰明;也許,你注 意到在堆棧和堆上創(chuàng)建對象的方法避免不了對構造函數(shù)的調用;也許,你想起 了我們最初的目標是為了避免這種對構造函數(shù)的調用;也許,你有個辦法可以 只用一個構造函數(shù)來搞掂一切;也許,你的眼前出現(xiàn)了這樣一段代碼:operator* 返回一個“在函數(shù)內部定義的靜態(tài)Rational 對象”的引用: // 寫此函數(shù)的第三個錯誤方法 inline const Rational& operator*(const Rational& lhs, const Rational& rhs) { static Rational result; // 將要作為引用返回的 // 靜態(tài)對象 lhs 和rhs 相乘,結果放進result; return result; } 這個方法看起來好象有戲,雖然在實際實現(xiàn)上面的偽代碼時你會發(fā)現(xiàn),不 調用一個Rational 構造函數(shù)是不可能給出result 的正確值的,而避免這樣的調 用正是我們要談論的主題。就算你實現(xiàn)了上面的偽代碼,但,你再聰明也不能 最終挽救這個不幸的設計。 想知道為什么,看看下面這段寫得很合理的用戶代碼: 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))) 注意當 operator==被調用時,總有兩個operator*剛被調用,每個調用返回 operator*內部的靜態(tài)Rational 對象的引用。于是,上面的語句實際上是請求 operator==對“operator*內部的靜態(tài)Rational 對象的值”和“operator*內部的 靜態(tài)Rational 對象的值”進行比較,這樣的比較不相等才怪呢! 幸運的話,我以上的說明應該足以說服你:想“在象operator*這樣的函數(shù) 里返回一個引用”實際上是在浪費時間。但我沒幼稚到會相信幸運總會光臨自 己。一些人——你們知道這些人是指誰——此刻會在想,“唔,上面那個方法, 如果一個靜態(tài)變量不夠用,也許可以用一個靜態(tài)數(shù)組⋯⋯” 請就此打!我們難道還沒受夠嗎? 我不能讓自己寫一段示例代碼來太高這個設計,因為即使只抱有上面這種 想法都足以令人感到羞愧。首先,你必須選擇一個n,指定數(shù)組的大小。如果 n 太小,就會沒地方儲存函數(shù)返回值,這和我們前面否定的那個“采用單個靜 態(tài)變量的設計”相比沒有什么改進。如果n 太大,就會降低程序的性能,因為 函數(shù)第一次被調用時數(shù)組中每個對象都要被創(chuàng)建。這會帶來n 個構造函數(shù)和n 個析構函數(shù)的開銷,即使這個函數(shù)只被調用一次。如果說"optimization"(最優(yōu) 化) 是指提高軟件的性能的過程, 那 么 現(xiàn) 在 這 種 做 法 簡 直 可 以 稱 為 "pessimization"(最差化)。最后,想想怎么把需要的值放到數(shù)組的對象中以及 需要多大的開銷?在對象間傳值的最直接的方法是通過賦值,但賦值的開銷又 有多大呢?一般來說,它相當于調用一個析構函數(shù)(摧毀舊值)再加上調用一 個構造函數(shù)(拷貝新值)。但我們現(xiàn)在的目標正是為了避免構造和析構的開銷啊! 面對現(xiàn)實吧:這個方法也絕對不能選用。 所以,寫一個必須返回一個新對象的函數(shù)的正確方法就是讓這個函數(shù)返回 一個新對象。對于Rational 的operator*來說,這意味著要不就是下面的代碼(就 是最初看到的那段代碼),要不就是本質上和它等價的代碼: inline const Rational operator*(const Rational& lhs, const Rational& rhs) { return Rational(lhs.n * rhs.n, lhs.d * rhs.d); } 的確,這會導致“operator*的返回值構造和析構時帶來的開銷”,但歸根 結底它只是用小的代價換來正確的程序運行行為而已。況且,你所擔心的開銷 還有可能永遠不會出現(xiàn):和所有程序設計語言一樣,C++允許編譯器的設計者 采用一些優(yōu)化措施來提高所生成的代碼的性能,所以,在有些場合,operator* 的返回值會被安全地除去(見條款M20)。當編譯器采用了這種優(yōu)化時(當前 大部分編譯器這么做),程序和以前一樣繼續(xù)工作,只不過是運行速度比你預計 的要快而已。 以上討論可以歸結為:當需要在返回引用和返回對象間做決定時,你的職 責是選擇可以完成正確功能的那個。至于怎么讓這個選擇所產生的代價盡可能 的小,那是編譯器的生產商去想的事。 |
| 最具人氣熱帖推薦 [查看全部] | 作者 | 回/看 | 最后發(fā)表 | |
|---|---|---|---|---|
|
[考研] 334分 一志愿武理 材料求調劑 +5 | 李李不服輸 2026-03-26 | 5/250 |
|
|---|---|---|---|---|
|
[考研] 297求調劑 +11 | 田洪有 2026-03-26 | 11/550 |
|
|
[考研] 一志愿南航 335分 | 0856 | GPA 4.07 | 有科研經歷 +6 | cccchenso 2026-03-29 | 6/300 |
|
|
[考研] 考研調劑 +5 | 小蠟新筆 2026-03-29 | 5/250 |
|
|
[考研] 375求調劑 +4 | 雨夏整夜 2026-03-29 | 4/200 |
|
|
[考研] 材料學碩333求調劑 +11 | 北道巷 2026-03-24 | 11/550 |
|
|
[考研] 0856求調劑 +7 | 楒桉 2026-03-28 | 7/350 |
|
|
[考研] 330分求調劑 +5 | qzenlc 2026-03-29 | 5/250 |
|
|
[考研] 0703化學調劑,求導師收 +9 | 天天好運來上岸?/a> 2026-03-24 | 10/500 |
|
|
[考研] 07化學280分求調劑 +10 | 722865 2026-03-23 | 10/500 |
|
|
[考研] 化學308分求調劑 +8 | 你好明天你好 2026-03-23 | 9/450 |
|
|
[考研] 305求調劑 +5 | 哇盧卡庫 2026-03-26 | 5/250 |
|
|
[考研] 322求調劑 +4 | 我真的很想學習 2026-03-23 | 4/200 |
|
|
[考研] 調劑求收留 +7 | 果然有我 2026-03-26 | 7/350 |
|
|
[考研] 271求調劑 +6 | 生如夏花… 2026-03-22 | 6/300 |
|
|
[考研] 281求調劑 +6 | Koxui 2026-03-24 | 7/350 |
|
|
[考研] 考研一志愿蘇州大學初始315(英一)求調劑 +3 | sbdksD 2026-03-24 | 4/200 |
|
|
[考研] 282求調劑 +3 | wcq131415 2026-03-24 | 3/150 |
|
|
[考研] 080500求調劑 +3 | zzzzfan 2026-03-24 | 3/150 |
|
|
[考研] 336化工調劑 +4 | 王大坦1 2026-03-23 | 5/250 |
|