條款一:指針與引用的區(qū)別
指針與引用看上去完全不同(指針用操作符‘*’和‘-》’,引用使用操作符‘?!撬鼈兯坪跤邢嗤墓δ?。指針與引用都是讓你間接引用其他對(duì)象。你如何決定在什么時(shí)候使用指針,在什么時(shí)候使用引用呢?
首先,要認(rèn)識(shí)到在任何情況下都不能用指向空值的引用。一個(gè)引用必須總是指向某些對(duì)象。因此如果你使用一個(gè)變量并讓它指向一個(gè)對(duì)象,但是該變量在某些時(shí)候也可能不指向任何對(duì)象,這時(shí)你應(yīng)該把變量聲明為指針,因?yàn)檫@樣你可以賦空值給該變量。相反,如果變量肯定指向一個(gè)對(duì)象,例如你的設(shè)計(jì)不允許變量為空,這時(shí)你就可以把變量聲明為引用。
“但是,請(qǐng)等一下”,你懷疑地問,“這樣的代碼會(huì)產(chǎn)生什么樣的后果?”
char *pc = 0;// 設(shè)置指針為空值
char& rc = *pc;// 讓引用指向空值
這是非常有害的,毫無疑問。結(jié)果將是不確定的(編譯器能產(chǎn)生一些輸出,導(dǎo)致任何事情都有可能發(fā)生),應(yīng)該躲開寫出這樣代碼的人除非他們同意改正錯(cuò)誤。如果你擔(dān)心這樣的代碼會(huì)出現(xiàn)在你的軟件里,那么你最好完全避免使用引用,要不然就去讓更優(yōu)秀的程序員去做。我們以后將忽略一個(gè)引用指向空值的可能性。
因?yàn)橐每隙〞?huì)指向一個(gè)對(duì)象,在C里,引用應(yīng)被初始化。
string& rs;// 錯(cuò)誤,引用必須被初始化
string s(“xyzzy”);
string& rs = s;// 正確,rs指向s
指針沒有這樣的限制。
string *ps;// 未初始化的指針
// 合法但危險(xiǎn)
不存在指向空值的引用這個(gè)事實(shí)意味著使用引用的代碼效率比使用指針的要高。因?yàn)樵谑褂靡弥安恍枰獪y試它的合法性。
void printDouble(const double& rd)
{
cout 《《 rd; // 不需要測試rd,它
} // 肯定指向一個(gè)double值
相反,指針則應(yīng)該總是被測試,防止其為空:
void printDouble(const double *pd)
{
if (pd)
{ // 檢查是否為NULL
cout 《《 *pd;
}
}
指針與引用的另一個(gè)重要的不同是指針可以被重新賦值以指向另一個(gè)不同的對(duì)象。但是引用則總是指向在初始化時(shí)被指定的對(duì)象,以后不能改變。
string s1(“Nancy”);
string s2(“Clancy”);
string& rs = s1; // rs 引用 s1
string *ps = &s1; // ps 指向 s1
rs = s2; // rs 仍舊引用s1,
// 但是 s1的值現(xiàn)在是
// “Clancy”
ps = &s2; // ps 現(xiàn)在指向 s2;
// s1 沒有改變
總的來說,在以下情況下你應(yīng)該使用指針,一是你考慮到存在不指向任何對(duì)象的可能(在這種情況下,你能夠設(shè)置指針為空),二是你需要能夠在不同的時(shí)刻指向不同的對(duì)象(在這種情況下,你能改變指針的指向)。如果總是指向一個(gè)對(duì)象并且一旦指向一個(gè)對(duì)象后就不會(huì)改變指向,那么你應(yīng)該使用引用。
還有一種情況,就是當(dāng)你重載某個(gè)操作符時(shí),你應(yīng)該使用引用。最普通的例子是操作符[]。這個(gè)操作符典型的用法是返回一個(gè)目標(biāo)對(duì)象,其能被賦值。
vector《int》 v(10); // 建立整形向量(vector),大小為10;
// 向量是一個(gè)在標(biāo)準(zhǔn)C庫中的一個(gè)模板(見條款35)
v[5] = 10; // 這個(gè)被賦值的目標(biāo)對(duì)象就是操作符[]返回的值
如果操作符[]返回一個(gè)指針,那么后一個(gè)語句就得這樣寫:
*v[5] = 10;
但是這樣會(huì)使得v看上去象是一個(gè)向量指針。因此你會(huì)選擇讓操作符返回一個(gè)引用。(這有一個(gè)有趣的例外,參見條款30)
當(dāng)你知道你必須指向一個(gè)對(duì)象并且不想改變其指向時(shí),或者在重載操作符并為防止不必要的語義誤解時(shí),你不應(yīng)該使用指針。而在除此之外的其他情況下,則應(yīng)使用指針假設(shè)你有
void func(int* p, int&r);
int a = 1;
int b = 1;
func(&a,b);
指針本身的值(地址值)是以passby value進(jìn)行的,你能改變地址值,但這并不會(huì)改變指針?biāo)赶虻淖兞康闹担?/p>
p = someotherpointer;//a is still 1
但能用指針來改變指針?biāo)赶虻淖兞康闹担?/p>
*p = 123131; // a now is 123131
但引用本身是以pass byreference進(jìn)行的,改變其值即改變引用所對(duì)應(yīng)的變量的值
r = 1231;// b now is 1231
盡可能使用引用,不得已時(shí)使用指針。
當(dāng)你不需要“重新指向”時(shí),引用一般優(yōu)先于指針被選用。這通常意味著引用用于類的公有接口時(shí)更有用。引用出現(xiàn)的典型場合是對(duì)象的表面,而指針用于對(duì)象內(nèi)部。
上述的例外情況是函數(shù)的參數(shù)或返回值需要一個(gè)“臨界”的引用時(shí)。這時(shí)通常最好返回/獲取一個(gè)指針,并使用 NULL 指針來完成這個(gè)特殊的使命。(引用應(yīng)該總是對(duì)象的別名,而不是被解除引用的NULL 指針)。
注意:由于在調(diào)用者的代碼處,無法提供清晰的的引用語義,所以傳統(tǒng)的 C 程序員有時(shí)并不喜歡引用。然而,當(dāng)有了一些 C++ 經(jīng)驗(yàn)后,你會(huì)很快認(rèn)識(shí)到這是信息隱藏的一種形式,它是有益的而不是有害的。就如同,程序員應(yīng)該針對(duì)要解決的問題寫代碼,而不是機(jī)器本身。
評(píng)論