程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> vc教程 >> 引用的作用

引用的作用

編輯:vc教程

眾所周知,引用作為函數參數可以避免參數對象的額外拷貝,對於非內置類型,一般而言可以獲得更高的效率,同時比指針更安全,語義也更清晰。但是除此之外引用有什麼特別的作用呢?在同一個作用域的引用,就像這樣:

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++的一個特殊手法,也許還有很多不為人知的作用等待我們去發掘呢~

注:文中的代碼並未經過嚴格測試,有興趣的讀者可以自己測試代碼的有效性。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved