在這篇文章里我將談?wù)劯鞣N層次的C++程序員經(jīng)常問及的四個(gè)問題。例如我很驚訝地發(fā)現(xiàn)還有很多程序員沒有意識(shí)到標(biāo)準(zhǔn)頭文件擴(kuò)展名.h的爭(zhēng)議,命名空間的恰當(dāng)用法以及引用臨時(shí)對(duì)象的規(guī)則。這些問題及其它將在這里進(jìn)行討論。
首先我們從解釋受非議的“XXX.h”頭文件名與現(xiàn)代、符合標(biāo)準(zhǔn)的“<XXX>”頭文件名記號(hào)之間的區(qū)別開始。接下來我們探索C++不為人知的角落,由于編譯器的局限性和關(guān)聯(lián)語(yǔ)言規(guī)則某些隱蔽的自然特性迷惑了許多程序員,比如逗號(hào)分隔表達(dá)式的意義與引用型變量的規(guī)則。最后我們將學(xué)習(xí)如何在程序啟動(dòng)前啟動(dòng)一個(gè)函數(shù)。
話題1:“iostream.h” or “iostream”?
很多C++程序員還在使用“iostream.h”代替新的符合標(biāo)準(zhǔn)的“iostream”庫(kù)。兩者有什么區(qū)別呢?首先,標(biāo)準(zhǔn)頭文件“.h”擴(kuò)展名在五年前就倍受爭(zhēng)議。在新代碼中使用有爭(zhēng)議的(過時(shí)的)特性永遠(yuǎn)都不是一個(gè)好主意。從本質(zhì)上看,“iostream”包括一系列支持窄字符與寬字符的模板化(templatized) I/O輸入輸出類,而相反地,“iostream.h”只支持字符流。第三,iostream接口的標(biāo)準(zhǔn)C++規(guī)范在許多細(xì)節(jié)方面進(jìn)行了變動(dòng)。因此,“iostream”的接口與實(shí)現(xiàn)同那些“iostream.h”是有區(qū)別的。最后,“iostream”是在std命名空間中定義的而“iostream.h”則是全局的。
由于這些本質(zhì)方面的不同,不能在同一程序中混合使用這兩種庫(kù)。作為一個(gè)規(guī)則,應(yīng)盡量使用“iostream”,除非你要處理的是只能與“iostream.h”兼容的遺留代碼。
話題2:將引用與右值綁定
右值和左值是C++編程的一個(gè)基本概念。本質(zhì)上來講右值是一個(gè)不可能出現(xiàn)在等號(hào)左邊的表達(dá)式。相反,左值引用一個(gè)對(duì)象(廣義范圍上的),或者一塊可讀寫的內(nèi)存。引用既可以指向右值也可以指向左值。然而,由于語(yǔ)言在處理右值上的限制,你也得在將引用指向右值是慎重考慮。
將引用與右值綁定像引用常量一樣也是被允許的。這條原則背后的原理是很顯而易見的:你無法改變右值,因?yàn)閷?duì)常量的引用確保程序不會(huì)通過這個(gè)接口改變右值。下面的例子,f()函數(shù)包含一個(gè)對(duì)常整型變量的引用。
1 void f(const int & i);
2
3 int main()
4 {
5 f(2); /* OK */
6 }
這段代碼將右值“2”做為函數(shù)f()的一個(gè)參數(shù)。代碼運(yùn)行時(shí),C++將創(chuàng)建一個(gè)值為2的臨時(shí)整型變量并將其與引用類型i綁定。這個(gè)臨時(shí)對(duì)象與它的接口將在 f()運(yùn)行期間一直存到直到函數(shù)f返回。函數(shù)f返回后它們立即被釋放。注意我們沒有將i聲明為常量類型,但是函數(shù)f仍有可能修改它的這個(gè)參數(shù),這將引起異常。因此最好是將引用與常量類型綁定。
同樣的規(guī)則適用于自定義類型。只有一個(gè)臨時(shí)對(duì)象為常量時(shí)才可以與引用類型綁定。
struct A{};
1 void f(const A& a);
2
3 int main()
4 {
5 f(A()); /* OK, binding a temporary A to a const reference*/
6 }
話題3:逗號(hào)表達(dá)式
逗號(hào)表達(dá)式是從C語(yǔ)言沿襲下來的。它就像你經(jīng)常使用的for循環(huán)與while-loop循環(huán)一樣。但是這里面的語(yǔ)法規(guī)則遠(yuǎn)不像看起來的那樣。首先,讓我們看看什么是逗號(hào)表達(dá)式。
一個(gè)表達(dá)式可以被逗號(hào)分隔為一個(gè)或若干個(gè)子表達(dá)式。例如:
1 if(++x, --y, cin.good()) /*three expressions*/
這條if語(yǔ)句被逗號(hào)分事為三個(gè)表達(dá)式。從C++的角度每個(gè)表達(dá)式都是合法的但是副作用產(chǎn)生了。整個(gè)逗號(hào)表達(dá)式的值是由最右邊的表達(dá)式?jīng)Q定的。于是只有con.good()的返回值是true時(shí)整個(gè)表達(dá)式的值才是true。這里有另一個(gè)關(guān)于逗號(hào)表達(dá)式的例子。
1 int j=10;
2 int i=0;
3
4 while( ++i, --j)
5 {
6 /*..repeat as long as j is not 0*/
7 }
話題4:在程序啟動(dòng)前調(diào)用函數(shù)
有些應(yīng)用需要在主程序啟運(yùn)前運(yùn)行啟動(dòng)函數(shù)。例如投票、支付和登錄函數(shù)必須在實(shí)際種程序啟動(dòng)前運(yùn)行。一個(gè)最簡(jiǎn)單的實(shí)現(xiàn)方法就是在一個(gè)全局對(duì)象的構(gòu)造函數(shù)里調(diào)用這些函數(shù)。因?yàn)槿謱?duì)象在程序的最開頭被隱式的創(chuàng)建,這些函數(shù)就可以在main()函數(shù)之前得到運(yùn)行。例如:
1 class Logger
2 {
3 public:
4 Logger()
5 {
6 activate_log();
7 }
8 };
9
10 Logger log; /*global instance*/
11
12 int main()
13 {
14 record * prec=read_log();
15 //.. application code
16 }
全局對(duì)象log在main()函數(shù)啟動(dòng)之前被創(chuàng)建。在它的構(gòu)造函數(shù)里,log調(diào)用了active_log()函數(shù)。于是,當(dāng)main()函數(shù)啟動(dòng)時(shí),它可以從日志文件中讀取數(shù)據(jù)。