foxmanzj 回復于:2004-04-18 17:07:33 |
關注這篇帖子,希望有會的不吝賜教。 |
xhl 回復于:2004-04-18 22:43:31 |
typedef int arri[4];
arri a1; arri &ref = a1; 這是什么語法,沒有見過。不知道是寫錯了,還是有這種用法! |
yangtou 回復于:2004-04-19 00:12:18 |
只學過一點c++,覺得樓主說的有道理 |
whyglinux 回復于:2004-04-19 11:36:28 |
[quote:81ce591d5f="THEBEST"]我在C++ PRIMER PLUS上看到作者在建議用指針還是引用時說出:如果數據對象是數組,則使用指針。因為這是唯一的選擇。
為什么?不可以將引用作用在數組上嗎? EG: typedef int arri[4]; arri a1; arri &ref = a1; 不是一樣可以? [/quote:81ce591d5f] 首先說明,象上面程序中的這種對數組名的引用是完全可行的。在定義的時候,只要是:"type" + "&" + "reference"這種形式,都可以用來定義一個對 type 類型的名叫 reference 的引用,不論 type 是基本類型、類還是其它自定義類型。上面定義的引用 ref 其作用就是用另外一個不同于數組名的名稱 ref 來代替原來的數組名 a1。除非你原來的數組名由于過長導致書寫不便或者難于記憶,從而想用一個較短易記的別名(引用)來代替它之外,這樣做沒有任何其它的好處,也就是說象上面那樣僅僅為了使用引用 ref 訪問被引用的對象 a1 從而定義這樣一個引用是沒有必要的。 引用的主要作用是用作函數的參數或者函數的返回值。(這樣做的好處就是:對于同一類型的引用(不同類型的引用例外,如下面第二部分程序中所示的那樣。但是在實際中幾乎用不到這種引用),直接訪問的是被引用的對象本身,虛實參數結合或者函數返回的時候不會額外產生拷貝對象到臨時對象的操作,與指針訪問具有同等的效率,但在使用上比指針訪問更加直觀。) 例如,我們可以用上面程序中提供的 arri 類型定義這樣一個函數:void f( arri& r ),r 就是對arri 類型的引用??梢赃@樣調用這個函數:f( a1 ),這樣在函數內對虛參 r 的操作實際上就是對 a1 數組的操作。一個特別的地方就是在函數 f 內可以通過虛參 r 得到數組的長度:sizeof( r )/sizrof( r[0] )!想一想這也是很自然的:引用只是一個別名,它繼承了被引用對象的全部屬性,與被引用對象是等同的。從而,arri類型實際上除了 int 之外,還包含了數組大小的信息。 但是這樣定義函數有著很大的弊端:函數 f 的應用受到了巨大的限制。由于類型 arri 實際上就是 int[4],表示一個有 4 個元素的整型數組,所以函數 f 的實參只能是具有 4 個元素的整型數組名,而對于另外長度的數組,即使是整型數組,也是不能作為實參傳遞給這個函數的。在實際應用中,大多數情況下數組的長度事先是不能確定的(一般僅能預先確定數組的最大長度。這已經不錯了;有時連數組的最大長度都無法事先估計),因此象上面那樣在函數參數中定義對數組的引用也就失去意義了??赡苁怯捎谶@個原因,所以才有了上面“如果數據對象是數組,則使用指針”的說法吧。 [quote:81ce591d5f="THEBEST"]還有在TCPL上講: 引用: 需要區分對變量的引用和對常量的引用,是因為在變量引用的情況下引進臨時量極易出錯,對變量的賦值將會變成對于----即將消失的=====臨時量的賦值。 怎么會出現這種情況呢?因為出現引用到臨時量上的情況有兩種: 1>不同類型之間引用。2>非左值。 那它非得通過const引用才可以呀。如果再通過引用來對被引用的量賦值呀? 怎么有機會去對臨時量的賦值呢?能對作用說的這個意思舉個例嗎? [/quote:81ce591d5f] 對于非左值的引用必須是 const 引用才可以。但是,對于不同類型之間的引用可以是非 const 的引用,舉例如下: [code:1:81ce591d5f] void ff( int& m ) // not this form: void ff( const int& m ) { cout << "in ff() at first, m = " << m << endl; m = 9; // m is a reference to a temporary int, not a reference to x; the temporary int is set to 9 by its reference m. cout << "in ff() after assigned, m = " << m << endl; } int main() { float x = 5.3; cout << "before ff(), x = " << x << endl; ff( (int&)x ); // x is a float while m is a reference to int cout << "after ff(), x = " << x << endl; } [/code:1:81ce591d5f] 在我的機器上運行結果如下。 before ff(), x = 5.3 in ff() at first, m = 1084856730 in ff() after assigned, m = 9 after ff(), x = 1.26117e-44 其中函數 ff 中參數 m 不是對 x 的引用了,而是對內存中一個臨時整型對象的引用,所以 x 得不到預期的結果(9.0)。之所以 x 得到改變,是因為 gclearcase/" target="_blank" >cc 編譯器把 m 引用的臨時對象的地址就設置為實參 x 的首地址,從而通過 m 改變了臨時變量的值,也就改變了實參 x 的值。其它編譯器是否也是這么處理的還有待證實。 對于 const 引用,不允許用這個引用改變被引用的對象;即使是由于類型轉換等原因 const 引用變成了對臨時生成的對象的引用,也不會允許對這個臨時對象作任何改變,即不存在向這個臨時對象的賦值問題。所以,const 引用的唯一作用就是只能使用被引用的對象,對被引用的對象實際上不能施加任何影響,即不能通過 const 引用改變被引用的對象,當然也就不存在上面所說的由于通過引用實際上是給臨時對象賦值而產生的問題了。 |
THEBEST 回復于:2004-04-19 14:10:21 |
[quote:41bd0d578d]因為 gcc 編譯器把 m 引用的臨時對象的地址就設置為實參 x 的首地址,從而通過 m 改變了臨時變量的值,也就改變了實參 x 的值。[/quote:41bd0d578d]gcc為什麼決定這樣做呢?
這樣做的話對x還產生影響。 而如果它只是改變臨時對象的話那不更好。(只是對用戶的感覺產生影響而不會影響到原來的x值?。? 它為什么把m引用的臨時對象的地址就設置為實參的首地址? 這樣的話就不存在那個什么臨時對象了呀?為了節省空間? |
whyglinux 回復于:2004-04-19 23:12:31 |
>> gcc為什麼決定這樣做呢?
>> 這樣做的話對x還產生影響。 >>而如果它只是改變臨時對象的話那不更好。(只是對用戶的感覺產生影響而不會影響到原來的x值?。? 如果gcc不這么做的話,你可能就又要問那為什么不決定這樣做呢?說到底,只是一個人為的規定而已。你在函數參數中用非 const 引用的目的就是想在函數中通過這個引用來操作原來被引用的對象的,從這一點上說,gcc的這種規定至少讓你能看到通過引用改變了被引用的對象(雖然有時可能不是你所期望的結果),比對被引用的對象毫無影響要好一些吧。當然這樣做也有它的缺陷,在下面我還會提到。 >> 它為什么把m引用的臨時對象的地址就設置為實參的首地址? 理由同上。 >> 這樣的話就不存在那個什么臨時對象了呀?為了節省空間? 在上面我給出的程序中,函數 ff() 中臨時對象就是以實參為首地址的一個整型空間,m 就是對這個臨時對象的引用(就象共用體一樣,float型的實參 x 和 int 型的臨時對象共用同一內存空間)。這樣做除了節省空間外,最主要的原因可能就是處理簡單吧。由于下述的兩個原因導致了使用不同類型之間的引用是無效的和不安全的,因此應該避免對不同類型之間的引用的使用??赡苁腔谶@一點,編譯器沒有對此作過多的處理。 1. 我上面也已經提到過,不同類型之間的引用雖然是合法的,但是由于類型轉換過程中臨時對象的產生,存在著使用上的問題。這就如同一個指向 int 型的指針,你一定要把它作為一個指向 float 型的指針使用一樣(通過強制類型轉換可以做到這一點),雖然是合法的,但是卻是應該避免的,因為這樣做一般沒有實際上的意義。 2. 在我給出的程序中給出了 float 型轉換到 int 型的引用,一般 sizeof( float ) >= sizeof( int ),所以還沒有嚴重的安全問題。如果是類似 char 型轉換到 int 型這樣的 sizeof( char ) < sizeof( int ) 的引用轉換,轉換之后變成了對整型的引用,會改變 char 之后的內存空間,這樣帶來的問題就很嚴重了。 同種類型的引用不產生臨時對象,沒有上述缺點,所以可放心使用。 |