ZWeily的小品文(四)C++入門教程(3) (轉)

worldblog發表於2007-12-14
ZWeily的小品文(四)C++入門教程(3) (轉)[@more@]§3 Namespace std

“師傅,能不能幫我過來看看?”

一聽這個就知道,我那個“好”徒弟Young又用她那種極有“磁性”的聲音在召喚我,一定是遇到了什麼問題,搞不定了。

“別叫我師傅,這樣感覺我很老一樣,還是叫我Weily吧。”我一邊說,一邊走過去,一看,她開著一個,似乎是在寫。仔細一看,原來在最基本、最著名的那個Hello World啊,呵呵,我鼻樑上的眼鏡可不是擺設用的,我也看出問題所在了。

“有什麼問題啊?”我故意裝作什麼都不知道的樣子。

“恩……我這個程式無法執行,我也不知道錯在哪裡,編譯不透過,老是報錯,而且,我這個程式是完全按照書上打的呀,應該沒錯的呀!可是就是……”

“喔?讓我看看。”我裝模作樣地湊過去看螢幕,螢幕上的程式是這樣的:

#include 
int main( )
{
 cout < return 0;
}

Young還把一本書拿到我面前,的確,那本書上的程式和螢幕上的一模一樣。

“你看,我沒打錯吧?”Young一臉無辜的樣子,難得看到她這樣,這個表情看上去楚楚可憐,與平時調皮的Young截然不同。

“你別急,我知道了。先提醒你一點,就是別迷信權威和書本,任何人都有犯錯的時候,這是一個優秀的程式設計師應該具有的素質。這個問題麼,讓我一點點來講給你聽。你先看看報的錯是什麼錯,記住,一個優秀的程式設計師能充分利用編譯器的查錯能力,並且,能從編譯器給出的錯誤資訊裡判斷出錯誤所在,所以,要學會讀懂出錯資訊,這是我對你的最基本的要求。”

“喔!它報的錯誤是’cout’: undeclared identifier。這個我以前在學C的時候見過。就是當我的程式裡使用了一個沒有宣告過的變數的時候,就會產生這種錯誤。”

“完全正確!你已經說出了錯誤的原因了。”

“啊?可是cout應該是在頭iostream裡面已經引入我這個程式了啊,那麼就不應該沒有宣告過啊?”Young又一次陷入了迷茫。

“其實這個問題牽涉到C++裡面的一個特性,就是關於名字空間(namespace)的作用。”

“namespace?……”

“讓我們還是從你這個程式看起。cout是一個ostream,也就是一個輸出流物件。它的確在iostream中定義了。但是,它的全稱應該寫成std::cout。這個std就是一個namespace,所有標準庫裡的東西都被封裝在std這個namespace裡。所以,編譯器在這裡就不能認出cout,因為它不在全域性名字空間裡。”

“那是不是我只要把cout改成std::cout就可以了啊?”

“恩,這樣改動後就能用了。不過這個問題就像孔乙己的那個茴香豆的茴字的四種寫法一樣,這個問題的解決方法也有四種解決方法。”

“四種?那另外三種是什麼?快告訴我啊!”看來我這個比喻引起了Young的興趣。

“第一種,就是將std這個名字空間引入全域性名字空間。這個方法只要在全域性裡,也就是任何一個之外,當然,最好放放在預處理之後,任何一個函式之前,這樣,任何一個函式里都可以直接使用std這個namespace裡面的東西,就像使用全域性物件一樣。例如你這個程式,就能改成這樣。”

#include 
using namespace std; //引入std這個namespace
int main( )
{
 cout < return 0;
}

“這個程式就能透過編譯,而且其他的標準庫裡的東西,也可以像這裡的cout一樣,直接使用。”

“喔!那麼這個方法不是很方便嗎?也不容易出錯啊!”

“並不是這樣。其實這麼做了之後,std這個namespace就失去了它存在的意義了。C++裡引入namespace的目的就是為了避免汙染全域性名字空間,簡單地說,就是為了避免和減少命名衝突。一旦一個程式寫大了,就很難避免重名,特別是多人合作的情況下。過去C中的解決方法只有靠人為的注意,並且加長名字,以避免重名。這樣做會使得一些名字看上去沒有意義或者難以理解,而程式設計師在寫程式的時候,也受這個問題的限制,不能自由地命名自己使用的變數或者函式。而有了namespace就不存在這些問題了,這就是C++引入namespace這個概念所帶來的便利。當然,這也使得C++裡很多名字看上去特別長。”

“喔,原來如此!本來我還準備以後就一直這麼用呢!”

“接下來我們討論第二種解決方案。這個方案和前面一種類似,也是引入名字空間,只是,它只引入需要的一部分。就像這個程式,我們只需要cout這個物件,所以,我們只需將std中的cout引入就可以了。就像這樣。”

#include 
using std::cout;
int main( )
{
 cout < return 0;
}

“喔,原來還可以這樣啊!這樣就可以避免了全部引入所帶來的汙染名字空間的問題了。那最後一種呢?”

“最後一種解決方法更簡單,但是,我先要你注意,這個方法今天看過就算了,不用去記住它,更不要使用它。先讓我們來看看用了這個方案的程式是什麼樣子的。”

#include  //注意這個標頭檔案與前面所寫的標頭檔案的區別
int main( )
{
 cout < return 0;
}

“這個方法的程式看上去似乎最簡短啊。對了,這個標頭檔案的問題也是我一直想問你的問題。我記得原來C裡面的那些標準庫的標頭檔案都是以.h結尾的啊,為什麼C++的標準庫的標頭檔案都沒有.h啊?”

“你能注意到這點很好,說明你在看書和寫程式的時候還在用腦子思考,並且能注意到許多人往往會忽視的細節問題。其實有.h這個字尾的標頭檔案和沒有.h的標頭檔案的最大的區別也在於namespace,有.h的標頭檔案裡的元素都是暴露在全域性中的,也就是沒有std這個namespace。而沒有.h的標頭檔案中的元素都是被封裝在std這個namespace中的。其實C++標準委員會早就去掉了標準庫裡每個標頭檔案的.h字尾,只是編譯器的生產廠商為了向前相容,為了能讓在標準出臺之前的程式都能用,於是仍然提供了.h的標頭檔案。記住,這些有.h的標頭檔案都不在C++標準之內,這也是我叮囑你不要去使用它的原因。還有一點,就是原來C中的那些標準庫標頭檔案,其實也存在相應的C++版本的,它們的命名規則很簡單,就是以c開頭,後面跟上原來的檔名,但是都沒有.h這個字尾。並且,其中的元素也都被封裝在std中了。”

“喔,原來還有這麼一個淵源啊!”

“是的。C++的發展過程中有很多類似的決策和選擇,現在標準裡定義的那些東西,都是專家們經過深思熟慮後才決定的,其中很多選擇和決定都有著它的道理,這個你有興趣的話可以去看看《The Design and Evolution of C++》這本書,它能告訴你C++為什麼是現在這樣的。其實因為C++有它的一套設計思想和設計哲學,標準庫裡的東西在設計上都符合這個思想,並且都保持著高效、的原則,所以,在你的程式中儘量地使用標準庫已經提供的東西,這樣你的程式碼將更為簡短、易讀,也更為安全可靠。記住這個原則。”

“恩,我記住了,並且以後會注意的。”

“好了,今天就講這些吧,關於標準庫的其他方面,以後我還會提起的。”

“好的,謝謝師傅!”

“和你說過多少遍了啊,別叫我師傅,我連女朋友都還沒有呢,我怕越叫越老的。就像記住我和你說的那些原則一樣,記住以後叫我Weily,別叫師傅!”

“遵命!師……”

“!”

“不是啦,我沒叫你師傅,我只是說‘是’”Young還是依舊那麼調皮。

“好啦,以後注意點。繼續寫你的程式,看你的書吧。”

“好的,Weily。今天多謝你了!”

“不用客氣啦,誰叫Solmyr把你交給了我呢……”

說完,回到自己的位子上,看著桌上那杯已經涼掉了的紅茶,哎……


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-993227/,如需轉載,請註明出處,否則將追究法律責任。

相關文章