C++語言常見問題解:#1 ~ #15

這是我從臺灣的http://www.cis.nctu.edu.tw/chinese/doc/research/c++/C++FAQ-Chinese/發現的《C++ Frequently Asked Questions》的繁體翻譯,作者是:葉秉哲,也是《C++ Programming Language》3/e繁體版的譯者,該文章是非常的好,出於學習用途而將它轉貼,本人未取得作者的授權,原文章的版權仍然歸屬原作者.

C++語言常見問題解
 

=======================================================
■□ 第2節:我該如何參與討論?(發信之前請務必一讀)
=======================================================

Q1:我該在哪個討論區中發問?

Comp.lang.c++ 是討論 C++語言本身最好的地方(譬如:C++ 程序設計﹑語法﹑風格
)。其它討論區是用來討論特定的系統(譬如:MS Windows 或是 UNIX),或是其它
和 C++語言不直接相關的主題(譬如:怎樣使用你的編譯器)。底下列出一些非常熱
門的討論區,並從它們的 FAQs 中摘錄些片斷,應該能讓您明瞭它們最常討論哪些課
題。

comp.os.ms-windows.programmer.tools
此區是用來討論有關 Windows 軟件開發系統工具的選擇及使用。
comp.os.ms-windows.programmer.misc
 此乃論及其餘 Windows 軟件開發之事項。
[有個 FAQ 列表,列出所有 comp.os.ms-windows.programmer.* 討論區]
FAQ 5.7.1. 在 DLL 中存取 C++ 的對象類別
FAQ 6.1.1. 以 MDI 子窗口做出對話框 [用 OWL]
FAQ 6.2.1. 把禁能的選項致能起來 [用 MFC]
FAQ 8.1.5. 使用 windows.h 的 STRICT 符號定義
FAQ 10. 程序設計參考資料

comp.os.msdos.programmer
許多信件都是關於程序語言產品的(主要是 Borland 和 Microsoft)。
FAQ 301. 怎樣才能讀取字符而不 [等待] Enter 鍵?
FAQ 412. 怎樣讀取﹑建立﹑更改及刪除磁盤標名?
FAQ 504. 怎樣設定 COM 埠,以用它來傳輸資料?
FAQ 602. C 程序怎樣才能送句柄給打印機?
FAQ 606. 怎樣才能得知 Microsoft 鼠標的位置及按鈕狀態?
FAQ 707. 怎樣寫常駐程序(TSR)工具?
FAQ B0. 怎樣連繫 [Borland, Microsoft] 等公司?
[注意:這份 FAQ 不在 rtfm.mit.edu 裏;而在 Simtel
(譬如 oak.oakland.edu) in /pub/msdos/info/faqp*.zip 以及 Garbo
(garbo.uwasa.fi) in /pc/doc-net/faqp*.zip]
 comp.os.msdos.programmer.turbovision [Borland 的文字模式應用程序骨架]

comp.unix.programmer
FAQ 4.5) 怎樣使用 popen() 開啓行程以讀寫之?
FAQ 4.6) 怎樣在 C 程序裏 sleep() 一秒以內?

comp.unix.solaris (包含 SunOS 4.x 和 Solaris)
FAQ 4) Signal 入門
FAQ 5) 等待子行程 Exit

 gnu.g++.help
FAQ: 到哪裏找 C++ 的 demangler(反簽名編碼器)?
FAQ: 哪裏有 Solaris 2.x 版的 gcc/g++ 位文件?
FAQ: 有 g++ 2.x 的文件嗎?
gnu.g++.bug [g++ 的臭蟲列表 -- 請見 g++ 的文件]

comp.lang.c
FAQ 1.10: 我搞胡塗了。NULL 保證一定是 0,但是 null 指針卻不是?
FAQ 2.3: 那麼,在 C 裏頭「指針和數組等價」是什麼意思?
FAQ 4.2: [爲什麼 "printf("%d/n," i++ * i++);" 有問題?]
FAQ 7.1: 怎樣寫一個接收不定數目自變量的函數? [stdarg.h 或是 varargs.h]
FAQ 10.4: 怎麼宣告一個指向某種函數的指針數組,而該函數的傳回值爲:
指向另一個傳回字符指針的函數?

並請參考看看 comp.graphics、comp.sources.wanted、comp.programming,以及
comp.object(它的 FAQ 是個很棒的 OOP 入門、術語觀念概論文件)。請記住:
comp.std.c++ 是專門討論和研議中的 ANSI/ISO C++ 標準方案(下文會提)“直接
”相關的事項。

同時到上述信區和 comp.lang.c++ 去問同一個問題,幾乎是沒必要的(你是知道的
,特定系統信區的讀者不用機器語言寫程序)。只因你的問題「真的很要緊」,就到
處發問,是個很壞的習慣。如果你在「正確的」信區沒得到迴音,且認爲你非得在這
兒發信不可,請至少考慮一下,將這兒的回信重導回原來那個適當的信區。

在任何信區發問之前,你應當先讀讀它的 FAQ。你想問的可能就在上面,這樣就可省
下你發信的時間,以及全世界數以千計的人類讀你的信的時間。回答已經是 FAQ問題
的人,可能會因爲白白浪費時間而煩擾不已;他們也可能會給你錯誤或不完整的解答
,因爲他們也沒看過 FAQ。

「常見問題解答」文件每天 24 小時都可由 anonymous ftp (rtfm.mit.edu 的
/pub/usenet/comp.what.ever) 或是 e-mail server (寄一則內容爲 "help" 的信到
[email protected]) 來取得。欲知詳情,請見 "Introduction to the
*.answers newsgroups" 這份文件,它在 news.answers 或 news.announce.newusers
(這兒還有許多必須一讀的文件)中找到。

========================================

Q2:我該怎麼提出「我的程序有毛病」的問題呢?

底下是一些建議,讓 comp.lang.c++ 的讀者能幫你解決程序設計的問題。

1. 請讀讀上一個問題,以確定你的問題是針對 C++語言本身,而和你的程序設計系
統(譬如:繪圖、打印機、設備……)或是編譯環境(譬如:「整合環境掛了」
、「怎樣消除xxxx警告訊息」、「怎樣連結鏈接庫」)完全無關。如果你想知道
爲什麼你 OWL程序中的虛擬函數 CmOk() 沒被呼叫到,你的問題可能比較適合放
在 Windows程序設計的信區。如果你能寫個獨立的小程序,而它會讓編譯器產生
和你那個 OWL程序同樣的錯誤訊息或行爲的話,就可以放到 comp.lang.c++ 了,
其它系統的 C++程序員可能幫得上忙。

2. 「信件標題」字段要有意義。像是「C++ 程序」這樣的標題太空泛了,「new 一
個多維數組的問題」就很好。不要用一堆驚歎號,窮嚷嚷着「救命啊」,或是開
玩笑的用「SEX SEX SEX」這種標題。如果你認爲該問題和你的編譯器有關,最好
在標題欄中道出編譯器和版本編號。

3. 列出完整的、可編譯得過去的程序代碼。要從人類的語言敘述裏,去除錯或是重建
回一個程序,是極爲困難的事。「完整的程序代碼」指的是:任何被用到的型別、
函數都要宣告出來,被用到的標頭檔都要 #include 進來……等等。請將程序代碼
裁減到只留必要的部份,我們並不需要那些執行起來(甚至連結時)“有用的”
東西,我們只須能重現出你的錯誤訊息(可能在不同的編譯器中)就行了。「可
編譯得過去」指的是:不要含有一堆未批註掉的 ... 這種刪節號,或是各行行首
的行號:

14: #include <iostream.h>
15: class Foo { ... }; // 像這樣就是很討人厭的東西!

將你的程序組織成線性結構,不要讓我們再切割、製造些標頭檔案。請仔細輸入
你的程序代碼--我們通常不容易判斷:某個地方只是你的打字錯誤,抑或它真的
就是你的問題所在。儘量改用編輯器的「剪貼」或「插入檔案」功能。

4. 列出你用的編譯器、編譯器版本,以及你使用的系統。我知道我剛剛說過:特定
系統的問題要去特定的信區發問,但和編譯器有關的信息,常常對偵查問題有幫
助(「喔,我記得 Acme 1.2 在這方面有很多毛病」),這也順便提醒了那些編
譯器的用戶:小心那些毛病。

5. 把編譯器、連結器的選項寫出來,以及你用來建程序所用的鏈接庫。

6. 把錯誤訊息和何處發生錯誤的資料寫出來。像是「虛擬函數不能用了」並沒告訴
我們這是個編譯時段、連結時段還是執行期的問題。如果這問題是執行期發生的
,請把它的行爲,和任何相關的系統設定信息列出來。

7. 在簽名檔中列出真的能用的 e-mail 地址。如果你信件的 "From:" 一欄有錯的話
,請通知你的系統管理者。在它修復前,於你的信件標頭中加入 "Reply-To:" 一
欄,填上你正確的 e-mail 地址。

8. 請讀讀這份 FAQ 的其它部份--可能你的問題,或是很相關的問題就在這兒。

謝謝您,並希望以上的建議能協助您找到問題的解答。


===================================
■□ 第3節:周遭的﹑管理上的事項
===================================

Q3:什麼是 OOP?什麼是 C++?

對象導向(OO)程序技術,是我們所知發展大型而複雜的軟件系統最好的方法。

C++ 是個對象導向的程序語言。C++ 可當成一個對象導向程序語言(OOPL),亦可只
當成一個“更好的 C 語言”來使用。不過,若你只把它當成“更好的 C”,你就無
法獲得對象導向程序設計的好處。

提一則 OO 的廣告詞:軟件工業刻正無法應付大型而複雜的軟件系統需求。但這正是
肇因於我們的「成果」:我們過去的成功促使大家要求得更多,不幸的是,這份市場
的渴求卻是「結構化」分析(analysis)﹑設計(design)和程序設計所無法滿足的
。因此,我們才得發展一個更好的典範(paradigm)。

========================================

Q4:C++ 的優點是什麼?

「C++ 的成長」:C++ 是到目前爲止最受歡迎的語言。每 7.5到 9個月 C++的使用者
都會加倍。「懂 C++」是個很好的求職資格(但你必須把它當成 OOPL,而不只是一
個更好的 C 來用才行)。

「封裝性 encapsulation」:藉由隱藏內部的數據結構,讓我們可以改變系統的某部
份,而不必更動其它部份。我們爲軟件組件(稱之爲 class,類別)提供一個安全的
接口,用戶只碰得到這個接口而已;而相對起來比較容易變動的接口「實作」部份,
就被封裝起來(就像被包在膠囊裏),以避免用戶過於依賴他一時的實作決定。在比
較簡單的 C 裏頭,可由模塊內的靜態(static)數據來辦到,以避免其它模塊存取
到它。

「多重案例 multiple instances」:典型的 C 語言「封裝」方法(剛纔有提),做
不到多重的資料案例(我們很難替模塊的 "static" 資料做出多重案例)。如果在 C
中要做到的話,我們得使用 "struct" 結構(但是它沒有「封裝性」)。在 C++裏,
我們可用 "class"(對象類別)來做到多重案例與封裝性:"public"公共部份包含了
它的接口(通常這裏會有個特別的函數:成員函數),"private" 私有部份包含了它
的實作細節(通常這兒就是內部數據結構的所在)。

「行內函數呼叫」:在 C 中,可以在 struct 裏放個 "void*"(該存取函數 [access
functions] 會用到指針轉型)來達到「封裝的 structs」。這樣會喪失型別安全性
,而且會造成過多的函數呼叫,即使你只存取結構內的小小字段(假如你允許直接存
取結構內字段的話,它內部的數據結構就很難再變更了,因爲你的程序有太多地方“
依賴”它以前的樣子)。函數呼叫的額外負擔不大,但是會累積起來。C++ 的類別允
許函數作 "inline" 行內擴展,就有以下好處:封裝的安全性,多重案例的方便
性,直接存取的速度。而且,編譯器也會檢查行內函數的參數,這就比 C 的
#define 宏更好了。

「多載運操作數」:C++ 能對對象類別的運操作數加以多載(overload),以合乎我們的
直覺(譬如,"myString + yourString" 可做字符串串接,"myDate++"可用來遞增日期
,"z1 * z2" 可將兩複數 z1 及 z2 相乘,"a[i]" 可用來存取 "a" 這個連結串行的
第 i 個元素……等等)。你甚至可以做出個“聰明的指針”(smart pointer),以指
向磁盤或其它地方去("x = *p" 可 dereference [解參用] 指針,也就可以在磁盤
中找到 p 所“指到”的地方,並傳回其值)。這可讓使用者以切近該問題的方式來
寫程序,而非以機器的語言來解題。

【譯註】STL (Standard Template Library) 就大量利用到「聰明的指針」功能。

「繼承性 inheritance」:我們還只是在表層而已,事實上,我們還沒進入「對象導
向」的部份呢!假設你有個 Stack 堆棧型態,有 push﹑pop 運算。若你還想要個
InvertableStack 型態,它“很像”Stack,只是它還有個 "invert" 運算。以 C 的
方式,你不是得修改現存的 Stack模塊(如果它在其它地方也用到的話,就麻煩了
),就是得把 Stack拷貝到另一個檔案,再加以修改之(這會導致過多重複的程序
碼、容易破壞到 InvertableStack 裏某些源自 Stack 的小地方,尤有甚者,得維護
雙倍的程序代碼)。C++提供了更乾淨的解決法:繼承。你可以說:「InvertableStack
繼承了 Stack的一切,且 InvertableStack又添加了 invert 運算。」這樣子就好了
!Stack本身仍然是封閉的(未被更動到),而 InvertableStack也沒重複 push/pop
等的程序代碼。

「多型」與「動態繫結」:OOP 真正的力量不僅是繼承性,還有把 InvertableStack
當成是一個 Stack來傳遞的能力。這是安全的,因爲(至少在 C++裏)此乃「是一個
……」的關係("is-a" relation),透過公共繼承達到的(亦即:InvertableStack
“是一個”Stack,且它還能自我 invert 反轉)。多型(polymorphism)與動態系
結(dynamic binding)最容易從實例來理解了,所以我提個典型的例子:繪圖軟件
得處理圓形﹑方形﹑矩形﹑多邊形及直線,這些都是「形狀 shape」。大部份繪圖軟
體的內部函數都需要個“形狀”的參數(相對於某些像是“方形”這種特定的形狀)
,譬如:當我們用鼠標選取某個圖形,它就可能被拖曳放到屏幕某處。多型和動態系
結讓程序能正確運作,即使編譯器只知道該參數是個「形狀」,而不知它到底是什麼
形狀。我們再假設剛纔提到的 "pick_and_drag(Shape*)" 函數於星期二編譯好了,
到了星期三,你打算再加個六邊形。聽起來很奇怪,但 pick_and_drag() 仍然能夠
處理這個六邊形,即使當 pick_and_drag() 編譯時六邊形還不存在!(若你明瞭
C++ 是怎麼做的,它就再也不驚異了--但它仍然是很方便的!)

========================================

Q5:誰在用 C++?

很多很多的公司及政府部門。相當的多。

統計上來看:當你正在讀這份 FAQ文字時,就有 5 個人正成爲 C++的程序員。

========================================

Q6:有任何 C++ 標準化方案在進行嗎?

有的;ANSI(美國的)和 ISO(國際的)組織正密切合作。ANSI-C++ 委員會稱爲
"X3J16" ,而 ISO C++ 標準團體稱爲 "WG21"。ANSI/ISO C++ 的標準過程中包含了
這些人:

AT&T, IBM, DEC, HP, Sun, MS, Borland, Zortech, Apple, OSF 等等等等。每次開
會約有 70 人,他們來自美、英、日、德、瑞典、丹麥、法國……(他們都有「區域
性」的委員會,派遣正式代表並主導「區域性」的會議)。

========================================

Q7:該到哪裏索取最新的 ANSI-C++ 標準草案?

ISO Committee Draft for C++ 以及 ANSI C++ Draft(將要供 public review 的文
件)可如此取得:
http://www.cygnus.com/~mrs/wp-draft

你也可以拿到 Postscript 和 Adobe Acrobat 的版本:
ftp://research.att.com/dist/stdc++/WP

也能拿到 HTML 和 ASCII 的版本:
ftp://ftp.cygnus.com/pub/g++

也能拿到書面版本:
X3 Secretariat
1250 Eye Street NW
Suite 200
Washington, DC 20005
202-626-5738

你也可以用 email:

[email protected] (Lynn Barra)

註明要索取最新的 "Draft Proposed American National Standard for Information
Systems -- Programming Language C++",文件編號 CD14882。它通常是用2日期的
FedEx(美國境內)來遞送的,所以很快就能收到。

========================================

Q8:C++ 對 ANSI-C 回溯兼容嗎?

幾乎是。

C++ 儘可能地和 C 兼容,但不能更兼容了。事實上,主要的不同在於 C++ 要求函數
原型:"f()" 宣告的是無參數的函數(在 C 裏,"f()" 和 "f(...)" 是一樣的)。
還有些細微的差別,像在 C++ 裏 sizeof('x') 等同於 sizeof(char),但在 C 裏面
卻是等同於 sizeof(int)。 而且,C++ 直接就把結構的卷標(tag)當成是型別的名
字,但 C 就需要加個 "struct" 字("typedef struct Fred Fred" 這種技巧仍然能
用,但在 C++ 中是累贅的)。

========================================

Q9:多久才能學會 C++?

像 Paradigm Shift 公司,成功地教授過標準的工業界「短期課程」,將大學一學期
的課壓縮到一週 40 小時。然而真正的精通得由實際經驗而來:沒有東西能取代時間
。需動手做的指定專題是必要的,因爲它們能將你的觀念「凝固成形」。

大約要 6-12 個月才能流利使用 C++/OOP,如果身邊有高手的話,費時會短些;反之
若沒有個“好的”通用型 C++對象鏈接庫,則會耗時更久。想成爲顧問級的高手,則
約需 3 年。

有些人卻根本辦不到。除非你是可造之材,且有強烈的個人驅動力,否則你也做不到
。「孺子可教」最起碼的要求是:你必須能「覺今是而昨非」。「驅動力」最起碼的
要求是:你願意多投入時間精力(改變思考的方式〔典範轉移 paradigm shift〕要
遠比學些新的東西來得困難)。


=========================
■□ 第4節:C++ 的基礎
=========================

Q10:什麼是類別(class)?

對象導向系統的基石。

類別是用來定義資料型態(data type)的,就像 C 的 struct 一樣。
以信息科學術語來說,一個型態包含了一組狀態(state),以及在狀態之間轉移的
動作行爲(operation)。因此 "int" 是個「型態」,因爲它有一組狀態,還有諸如
「加兩個整數」、「整數相乘」等等的運作行爲。同樣的,「類別」提供一組(通常
是公共的)運算,及一組(通常是非公共的)數據域位,以代表該型態的案例所擁有
的抽象值。以 C 的角度來看,類別就是其成員(members)皆預設爲 "private" 的
struct。

把 "int" 想成是個類別,它擁有 "operator++" 等等的運作行爲(method)。

========================================

Q11:什麼是對象(object)?

一塊賦有某種語意的儲存空間。

在宣告 "int i;" 之後,我們稱「i 是個 int 型態的對象」。在 C++/OOP 裏,「物
件」通常意指「類別的案例(an instance of a class)」,因此類別定義了數個物
件(案例)的行爲。

========================================

Q12:什麼是參考(reference)?

一個對象的“別名”(alias,另一個名稱)。

參考通常用於傳址呼叫(pass-by-reference):

void swap(int& i, int& j)
{
 int tmp = i;
i = j;
j = tmp;
}

main()
{
int x, y;
//...
swap(x,y);
    }

在這裏 "i" 和 "j" 分別是是 main 函數中 "x" 與 "y" 的別名,換句話說,"i" 就
是 "x"--不是個指向 "x" 的指針,也不是 "x" 該值的複製品,而它的的確確就是
"x" 本身。你對 "i" 做的任何動作,都會反映到 "x" 上;反之亦然。

從最底層來看,參考最常用指針來實作,它的效果有點像 C 裏頭的「傳指針呼叫」
(pass-by-pointer),但 "&" 取址運操作數由呼叫者換到被呼叫者之處了,你也要刪
去所有的 "*" 運操作數。

========================================

Q13:如果設定某值給參考會怎麼樣?

會更動到被參考者(referrent,該「參考」所參考到的對象)。

記住:「參考」就是「被參考者」,因此動了參考就會改動到被參考者(「參考」是
「被參考者」的左值 "Lvalue"〔出現在設定陳述的左邊〕)。

更進一步,我們也允許參考被傳回。這樣子函數呼叫就可放在設定陳述的左邊,這對
運操作數多載的場合很有用。

========================================

Q14:怎樣才能將參考改設成別的對象?

沒有辦法。

和指針不同,一旦參考被繫結到某個對象,它就不能再被改設到其它對象去。「參考
」本身不是一個對象(它自己沒有地址;「取參考的地址」只會得到被參考者的地址
;切記:「參考」就是「被參考者」)。

將「參考」與「被參考者」分離開來是不可能的。

========================================

Q15:何時該用參考,何時又該用指針?

可以時,用參考;必要時,就用指針。

當你不需要“重設”它時(見前一個問題),參考會比指針好。這通常意味着:在物
件類別的公共接口中參考最有用。參考大多用於對象的表層,而指針則多用於裏層。

但有一個例外:當函數參數或傳回值需要一個「臨界」(sentinel)的參考值時,最
好是用指針來做,以 NULL 指針做爲一個特別值(「參考」應該是個實質對象的「別
名」,而不是個解參用的〔dereferenced〕NULL 指針)。

注意:老資格的 C 程序員不喜歡參考,因爲在父程序的地方,「參考」的語意並不
是那麼明顯。然而有了些 C++經驗後,會發現這正是一種「信息隱藏」的作法,是利
而非弊。好比說,程序員應該以切近該問題的方式來寫程序,而非以機器的語言來解
題。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章