出處:http://www.360doc.com/content/08/0726/16/70532_1472760.shtml
以下译自Dan Saks的文章 References vs. Pointers 英文原文
———————————————————————————
深层含义
int i = 3;
则有:
int *pi = &i;
声明 pi 为一个指针类型的对象,并且是一个”指向int整型的指针”,它的初始值为对象i的地址。而另一方面:
int &ri = i;
声明 ri为一个reference类型的对象,并且也是一个指向整型的reference,它指向的是i。我们可以看到pointer和reference的声明有显著的不同,但这并不是决定何时使用哪一个的根据。决定的真正依据是当它们被用在表达式中时其显示的不同决定了使用哪一个合适
*p = 4;
将i的值变为4; 而使用reference ri,我们只需要直接写:
ri = 4;
就可以同样将i的值变为4 。
enum day{
Sunday, Monday, …
};
day x;
day operator++(day d);
并不能够得到想要得效果。 这个函数通过值传递参数(pass by value),这就意味着函数内看到的是参数的一个拷贝,而不是参数本身。为了使函数能够改变其操作数(operand)的值,它必须通过指针或reference来传递其操作数。
day *operator++(day *d);
它通过将增加后的值存储到*d里面来使函数改变日期(day)的值。但是,这样你就必须使用像表达式++&x这样来调用这个操作符,这看起来不太对劲儿。
day &operator++(day &d)
{
d = (day)(d + 1);
return d;
}
day *operator++(day *d);
是不能 通过编译的。每个重载的操作符函数必须或者是一个类的成员, 或者使用类型T、 T & 或 T const & 为参数类型,这里T是一个类(class)或列举(enumeration)类型。也就是说,每一个重载操作符必须以类或列举类型为参数类型。指针,即使是指向一个类或列举类型对象的指针,也不可以用。C++ 不允许在重载操作符时重新定义内置操作符的含义,包括指针类型。因此,我们不可以定义:
int operator++(int i); // 错误
因为它试图对int重新定义操作符 ++ 的含义。 我们也不可以定义:
int *operator++(int *i); // 错误
因为它试图对 int * 重新定义操作符 ++ 的含义。
References vs. const pointers
int &ri = i;
将 ri 绑定到 i 。然后下面的赋值:
ri = j;
并不是把 ri 绑定到 j ,而是将 j 中的值赋给 ri 指向的对象,也就是赋给 i 。
void f()
{
int &r = i;
…
}
void f()
{
int &r; //错误
…
}
void f()
{
int *const p = &i;
…
}
void f(){
int *const p; // 错误
…
}
Null references
int i, j;
swap(i, j);
void swap(int *v1, int *v2)
{
int temp = *v1;
*v1 = *v2;
*v2 = temp;
}
swap(&i, NULL);的后果很可能是不愉快的。
void swap(int &v1, int &v2)
{
int temp = v1;
v1 = v2;
v2 = temp;
}
swap(i, j);
更安全?
int *p;
…
int &r = *p;
int &f()
{
int i;
…
return i;
}
以指针或reference为参数的C++函数定义与调用对照表
假设有对象定义:
MyObjectType obj1;
1. 值传递:
如果函数定义为:
void myFunction( MyObjectType obj);
函数调用:
myFunction(obj); //函数以外对象obj的值不会 改变
2. reference传递:
如果函数定义为:
void myFunction( MyObjectType &obj);
函数调用:
myFunction(obj); //函数以外对象obj的值会 改变
3. 指针传递:
如果函数定义为:
void myFunction( MyObjectType *obj);
函数调用:
myFunction(&obj); //需要dereference(&), 函数以外对象obj的值会 改变
参考资料:
- Saks, Dan. “Introduction to References,” Embedded Systems Programming, January 2001, p. 81.
- Saks, Dan. “References and const“, Embedded Systems Programming February 2001, p. 73.
void swap(int &x, int &y) // call by reference { int temp; temp = x; x = y; y = temp; } main() { int i, j; cout << "Input 2 numbers:" << endl; cin >> i >>j; if( i > j ) swap(i, j); cout << "The smaller number is " << i << endl; cout << "The larger is " << j << endl; };
- 函數呼叫 swap(i, j) 時, 引數的 傳遞 如同
int &x=i; int &y=j;。 - 引數 x 為 變數 i 的 別名, y 為 變數 j 的 別名。
- 於函數 swap 中, 改變 引數 x 與 y 的值, 變數 i 與 j 也隨著 改變。
- 該程式 執行 的 結果 為:
Input 2 numbers: 100 50 The smaller number is 50 The larger is 100
class CA { private: int y; public: int x; CA():x(0){} // x is initialized to 0 int &f(int y) { x = y+20; return x;} int &g() { return y;} }; main() { CA a; a.x = 2; a.f(2) = a.x + 3; // 函數呼叫 位於 等式 左側 a.g() = 10; // private 變成 public cout << "a.x = " << a.x << endl; cout << "a.y = " << a.g() << endl; };
說明:
- 函數呼叫 a.f(2) 時, a.x = 22, 又 a.f(2) 函數值 為 a.x, 因此,
a.f(2) = a.x + 3;
就 如同 下列 程式碼:
a.x = y + 20;
a.x = a.x + 3; - 函數呼叫 a.g() = 10; 時, 即 a.y = 10;, 此時, private data member y 變成 public data member。
- 該程式 執行 的 結果 為:
a.x = 25
a.y = 10
int &f() { static int x = 0; x++; // x 可用來記錄函數被呼叫的次數 return x; } main() { int i; f();f();f(); i = f(); cout << "函數 f() 被呼叫 " << i << " 次" << endl; i = f() = 0; // 從新設定 static 變數 x 為 0 cout << "從新設定後, 函數 f() 被呼叫 " << i << " 次" << endl; };
說明:
函數 f() 被呼叫 4 次 從新設定後, 函數 f() 被呼叫 0 次
=========================================================
前面一篇講了pointer,這篇要講只有C++才有的reference,
C是沒有reference的,相較於pointer,reference顯得平易近人多了!
reference有個很大的特性,它的意思是別名(alias)的意思!
reference不像pointer記錄著變數的記憶體位置,它只是變數的別名!
下面用個簡單的例子講解:
1 | int iValue = 2; |
2 | int &iReference = iValue; |
3 | cout << iReference << endl; //會印出2 |
4 | cout << &iReference << endl; //會印出iValue 的記憶體位置 |
5 | cout << &iValue << endl; //會印出iValue本身自己的記憶體位置 |
上面的範例宣告了一個整數變數(iValue),和一個參考變數(iReference) 第一行會先印出2,
因為前面說過參考只是別名, 意思就是說iReference是iValue的別名,
在白話一點iReference等於iValue,
所以第二行會印出iReference(也就是iValue)的記憶體位置,假設印出0x28ff44(每台電腦都不一定是這個值)
第三行就是印出iValue自己的記憶體位置,所以!!!它也會印出0x28ff44!! 跟第二行一樣!! (因為他們兩個相等咩)
reference跟pointer不一樣的還有幾點,
reference一定要有初始值!!!
取值不需要加上*!!
不過其實參考使用的時機大部分是用在函數時, 因為是別名,所以可以避免複製大量的變數到函數去(就算是pointer函數,也會複製)
下面是一個簡單的參考函數範例:
01 | void fnReference(int &iValue){ |
02 | iValue = iValue +1; |
03 | cout << iValue << endl; |
04 | } |
05 | main(){ |
06 |
07 | int iValue = 2; |
08 | fnReference(iValue); |
09 | cout << iValue << endl; |
10 |
11 | system(“pause”); |
12 | return 0; |
13 | } |
在上面的範例我們宣告了一個函數(fnReference),其傳遞參數為參考型態(&iValue)
在主程式中(main),傳遞了單純的整數變數進去,
跟pointer不一樣,pointer是傳記憶體位置進去,
參考就直接傳普通的變數進去,
我們在函數中把傳遞進去的參數+1,然後印出,會印出3,
回到主程式以後,在印出iValue,一樣會印出3。
因為我們是傳遞參考,這就是call by reference!
最後,reference還有一個跟pointer不一樣的點!
就是reference不能用來進行運算!!!!!