分析以下程序的執行結果
#include<iostream.h>
class Sample
{
int x,y;
public:
Sample(){x=y=0;}
Sample(int i,int j){x=i;y=j;}
void copy(Sample &s);
void setxy(int i,int j){x=i;y=j;}
void print(){cout<<"x="<<x<<",y="<<y<<endl;}
};
void Sample::copy(Sample &s)
{
x=s.x;y=s.y;
}
void func(Sample s1,Sample &s2)
{
s1.setxy(10,20);
s2.setxy(30,40);
}
void main()
{
Sample p(1,2),q;
q.copy(p);
func(p,q);
p.print();
q.print();
}
解:
本題說明對象引用作為函數參數的作用。Sample類中的copy()成員函數進行對象拷貝。在main()中先建立對象p和q,p與q對象的x,y值相同,調用func()函數,由于第2個參數為引用類型,故實參發生改變;而第1個參數不是引用類型,實參不發生改變。所以輸出為:
x=1,y=2
x=30,y=40
===============================================================================================
眾所周知,引用作為函數參數可以避免參數對象的額外拷貝,對于非內置類型,一般而言可以獲得更高的效率,同時比指針更安全,語義也更清晰。
?? 但是除此之外引用有什么特別的作用呢?
?? 在同一個作用域的引用,就像這樣:
?? void f()
?? {
?? int i = 0;
?? int &ri = i; //這里。
?? //...
?? }
??
?? 事實上,在f的內部,需要操作i的地方,完全可以直接使用i,而不必要使用ri間接操作,使用i在語義上更明確。而混合使用i和ri反倒容易引起邏輯的混亂。
?? 似乎這是一個雞肋,但是其實不是。
?? 這里引用乾坤一笑文章中的例子:
??????????????? 例一、用C宏,書寫代碼更簡潔
??????????????? 這段代碼寫網絡程序的朋友都很眼熟,是Net/3中mbuf的實現。
???????????????
??????????????? struct mbuf
??????????????? {
??????????????????? struct m_hdr mhdr;
??????????????????? union {
??????????????????????? struct
??????????????????????? {
??????????????????????????? struct pkthdr MH_pkthdr; /* M_PKTHDR set */
??????????????????????????? union
??????????????????????????? {
??????????????????????????????? struct m_ext MH_ext; /* M_EXT set */
??????????????????????????????? char MH_databuf[MHLEN];
??????????????????????????? } MH_dat;
??????????????????????? } MH;
??????????????????????? char M_databuf[MLEN];??????? /* !M_PKTHER, !M_EXT*/
??????????????????? } M_dat;
??????????????? };
???????????????
??????????????? 上面的代碼,假如我想訪問最里層的MH_databuf,那么我必須寫M_dat.MH.MH_dat.MH_databuf; 這是不是很長,很難寫呀?這樣的代碼閱讀起來也不明了。其實,對于MH_pkthdr、MH_ext、MH_databuf來說,雖然不是在一個結構層次上,但是如果我們站在mbuf之外來看,它們都是mbuf的屬性,完全可以壓扁到一個平面上去看。所以,源碼中有這么一組宏:
???????????????
??????????????? #define m_next????? m_hdr.mh_next
??????????????? #define m_len?????? m_hdr.mh_len
??????????????? #define m_data????? m_hdr.mh_data
??????????????? ... ...
??????????????? #define m_pkthdr??? M_dat.MH.MH_pkthdr
??????????????? #define m_pktdat??? M_dat.MH.MH_dat.MH_databuf
??????????????? ... ...
???????????????
??????????????? 這樣寫起代碼來,是不是很精練呢!
??? 這里用宏很巧妙的解決了訪問深層數據的問題,但是宏的固有缺點也被引入了代碼中,同時,如果其他地方無意中引用了這個宏定義的頭文件,而且恰好使用了名為m_pktdat的數據成員,那這個宏帶來的后果可就不是我們想要的了。
??? 事實上用引用也可以達到類似的效果,不過必須是在使用的時候。由于引用不是標準C的組成部分,所以這只是一個C++技巧。
??? //假如代碼是這樣的:
??? mbuf m; //這里的mbuf就是前面的struct mbuf。
??????????? //如果要使用MH_ext成員,可以這樣:
??? m_ext &MH_ext = m.M_dat.MH.MH_dat.MH_ext;
??? //然后你的代碼中就可以直接使用MH_ext作為m.M_dat.MH.MH_dat.MH_ext的替代品了。
??? 也許看起來不是很自然,不過這無疑是一種很直接的方法。你還可以通過一個const引用來在邏輯上避免無意的寫操作。
??? 實際的“面向對象”的C++代碼中,不推薦直接數據成員的訪問,取而代之的是使用Get()和Set()方法存取數據,有些人只使用Get,通過返回一個成員的引用來達到讀寫數據成員的雙重目的,這時候,你可以在外部定義一個引用接受函數的返回,從而避免每次都要寫(XXX.Get()).Get()這種拖沓的語句來訪問一個深層的成員。
???
??? 引用的另一個作用,就是“別名”。別名是引用的另一種翻譯,很明確的表達了引用的另一個作用。
??? 僅僅是為了代碼的可讀性:
??? //下面的代碼
??? int i = 0,j = 0;
??? //...
??? for( i = 0; i < 10; i++)
??????? for( j = 0; j < 10; j++ )
??????????? a[i][j] = 0;
??? //你能明白這段代碼的含義嘛?有點困難,i和j的含義是不明確的,無法一眼看透。
??? 假如改成這樣:
??? const int width = 10;
??? const int height = 10;
??? //...
??? int i = 0,j = 0;
??? //...
??? int &line = i;
??? int &row = j;
??? for(line = 0;line < height;line++)
??????? for(row = 0;row < width;row++)
??????????? a[line][row] = 0;
??? //是不是好了一點?
??? //這并不是一個典型的例子,因為i和j的定義是任意的,某些情況你必須使用別人給定的名稱很郁悶的變量,而他們又必須用來表達截然不同的含義,這時候一個引用往往可以讓你清爽很多。
???
??? 再看下面這個例子:
??? class CA
??????? {
??????????????? int m_i;
??????? public:
??????????????? int &i;
??????????????? int const &c_i;
??????????????? CA():i(m_i),c_i(m_i){};
??????? };
??? 這是一個簡單的類,與所謂的“面向對象”的方法不同,這里使用引用實現內部數據的公用接口。這個手法用來對應 乾坤一笑 的另一段話:
???????? 這就是偶說的PME模型的問題了,delphi、java、c#之類的語言都提供一種叫做屬性的語法,大概是這個樣子的:
??????????????? class A
??????????????? {
??????????????? property int x
??????????????? {
??????????????? get {return x;}
??????????????? set {x = value;}
??????????????? }
??????????????? };
???????????????
??????????????? A a;
??????????????? 這樣, 就可以這么訪問了 a.x = 8; int b = a.x;
???????????????
??????????????? 這比用 a.setx(8); b=a.getx();直觀多了。
??? 你可以用 CA a; a.i訪問CA的私有數據成員,達到像屬性方法那樣的效果。但是這個方法在VC6下的表現卻不盡如人意,因為它存儲了一個指針用來取代語法上的引用,這導致類體積不必要的擴張,是我們所不希望看到的。也許在實現上確實存在難度,不過還是希望有更好的編譯器能實現真正意義的引用接口。
??? 這個例子其實是上面深層數據成員訪問的一個引申。
??? 引用,作為C++的一個特殊手法,也許還有很多不為人知的作用等待我們去發掘呢~
???
??? 注:文中的代碼并未經過嚴格測試,有興趣的讀者可以自己測試代碼的有效性。
??? 本文引用的? 乾坤一笑 的內容均出自:
??? http://blog.vckbase.com/smileonce/archive/2005/03/27/4081.html