C++ 學習問題集錦
譯者的話:盡管我已非常用心,力求完美,但受水平所限,錯誤在所難免,還請各路高手不吝斧正。郵箱地址:Antigloss at 163 dot com。本譯文是對以前葉豐的譯文的補充,之前葉豐翻譯過的內容我沒有重譯,故亦沒有出現于本頁面,想看的朋友可以自行搜索一下。呃……其實有少量譯文使用了葉豐的翻譯,那些我會用灰色字表明。類的偉大之處是什么?
類可以幫助你組織代碼和分析程序。你可以大致理解為,類可以讓你避免犯錯,而如果你犯錯了,類使你易于尋找錯誤。也就是說,類大大提高了代碼的可維護性。
類是思想和觀念的代碼形式。類的對象是思想的具體實例在代碼上的體現。沒有類,讀代碼的人只能猜測數據和函數的關系——類能清楚地表明這種關系,并且能被編譯器理解。有了類,程序的高層結構就更能反映在代碼上,而不單單是在注釋中。
一個設計良好的類能為用戶提供簡潔易用的接口,并將其內部結構隱藏起來,用戶根本不必了解其內部結構。如果內部結構不應該被隱藏——例如,因為用戶需要隨意改變類中的任何數據成員——你可以把這種類認為是“普通的老式數據結構(data structure)”;例如:
struct Pair {
Pair(const string& n, const string& v) : name(n), value(v) { }
string name, value;
};
注意,數據結構也可以使用輔助函數,例如構造函數。
設計類時,思考類有哪些方面在任何時刻對其每個對象都是通用的,這會很有用。這種通用的屬性被稱之為不變要素(invariant)。例如,vector 的不變要素是,其內部有一個指針,指向一系列元素,這些元素的數目保存于一個整型變量。每個構造函數都有責任去構建類的不變要素,這樣成員函數才能依賴這些不變要素。成員函數退出時必須保持不變要素的有效性。這種思維方式對于管理資源的類來說更是特別有益,如管理鎖(locks)、sockets 和文件的類。例如,一個處理文件的類的不變要素是,它有一個指針,指向一個打開的文件。該類的構造函數負責打開文件,而其析構函數負責釋放構造函數獲取的資源。例如,該類的析構函數負責關閉構造函數打開的文件:
class File_handle {
public:
File_handle(const char* n, const char* rw)
{ f = fopen(n,rw); if (f==0) throw Open_failure(n); }
~File_handle() { fclose(f); } // destructor
// ...
private:
FILE* f;
};
如果你未曾使用過類,你會覺得這番說明有些地方相當費解并且會低估類的用處。要尋找例子,和所有優秀的教科書一樣,TC++PL(C++ 程序設計語言)里有很多例子;例如,漫游標準庫。大多數現代 C++ 庫都包含類(當然還有其它東西),庫使用指南是尋找有用的類的例子的最佳場所之一。
原文地址:http://www.research.att.com/~bs/bs_faq.html#class
什么是面向對象編程?它的偉大之處是什么?
面向對象(object oriented, OO),面向對象編程(object-oriented programming, OOP),以及面向對象編程語言(object-oriented programming languages, OOPL)的定義多種多樣。想了解我對 OO 的詳細看法,請閱讀 C++ 為什么不僅僅是面向對象的語言。我在那里寫道,OOP 這種編程風格起源于 Simula(約 40 年以前!),它依賴于封裝(encapsulation)、繼承(inheritance)以及多態(polymorphism)。就 C++(及許多其它源于 Simula 的語言)而言,OOP 的意思是利用類層級(class hierarchies)及虛函數進行編程,從而可以通過精制的接口操作各種類型的對象,并且程序本身也可以通過派生(derivation)進行功能增量擴展。
你可以從類的偉大之處是什么這篇文章了解到“普通類(plain classes)”的偉大之處。把多個類組織成類層級是為了表達類之間的層次關系并且利用這些關系簡化代碼。
要想真正理解 OOP,就要看些例子。例如,你可能有兩個(或者更多)設備驅動共用一個公共接口:
class Driver { // 公共驅動接口
public:
virtual int read(char* p, int n) = 0; // 從設備中讀取最多 n 個字符到 p
// 返回讀到的字符總數
virtual bool reset() = 0; // 重置設備
virtual Status check() = 0; // 讀取狀態
};
Driver 僅僅是一個接口。它沒有任何數據成員,而其成員函數都是純虛函數。通過這些接口就可以使用某個驅動;不同類型的驅動負責對這個接口進行相應的實現:
class Driver1 : public Driver { // 某個驅動
public:
Driver1(Register); // 構造函數
int read(char*, int n);
bool reset();
Status check();
// 實現細節
};
class Driver2 : public Driver { // 另一個驅動
public:
Driver2(Register);
int read(char*, int n);
bool reset();
Status check();
// 實現細節
};
注意,這些驅動含有數據成員,可以通過它們創建對象。它們實現了 Driver 中定義的接口。不難想象,可以通過這種方式使用某個驅動:
void f(Driver& d) // 使用驅動
{
Status old_status = d.check();
// ...
d.reset();
char buf[512];
int x = d.read(buf,512);
// ...
}
這里的重點是,f() 不需要知道它使用的是何種類型的驅動;它只需知道有個 Driver 傳遞給了它;也就是說,有一個接口傳遞給了它。我們可以這樣調用 f() :
void g()
{
Driver1 d1(Register(0xf00)); // create a Driver1 for device
// with device register at address 0xf00
Driver2 d2(Register(0xa00)); // create a Driver2 for device
// with device register at address 0xa00
// ...
int dev;
cin >> dev;
if (dev==1)
f(d1); // use d1
else
f(d2); // use d2
// ...
}
注意,當 f() 使用某個驅動時,與該驅動相對應的操作會在運行時被隱式選擇。例如,當 f() 得到 d1 時,d.read() 使用的是 Driver1::read();而當 f() 得到 d2 時,d.read() 使用的則是 Driver2::read()。這被稱為運行時調度或者動態調度。本例,f() 無法得知調用的是何種設備,因為那是根據輸入選擇的。
請注意,OOP 并非萬能藥。不要簡單地把“OOP”等同于“好”。如果你的問題的基本要素中沒有與生俱來的層級關系,那么類層級和虛函數對你的代碼不會有任何幫助。 OOP 的優勢在于類層級可以有效地表達很多問題;OOP 的主要弱點在于太多人設法強行用層級模式解決問題。并非所有問題都應該面向對象。也可以考慮使用普通類(plain class)、泛型編程和獨立的函數(就像數學、C,以及 Fortran 中那樣)作為解決問題的方案。
原文地址:http://www.research.att.com/~bs/bs_faq.html#oop
何謂泛型編程?其偉大之處何在?
泛型編程(Generic Programming, GP)是一種基于參數化(parameterization)的編程技巧:可以使用類型參數化另一種類型(例如,vector 的元素類型就是通過參數確定的);算法也可以參數化另一種算法(例如,使用比較函數參數化排序函數)。GP 的目的是將有用的算法或者數據結構盡可能地一般化,并使其最優化。例如,如果沒有 GP,你必須為整型 vector 專門寫一個類;然后,為了尋找其中的最大值,你又得為它寫一個專門的函數。但是,使用 GP 可以把一切變得更美好:只需要寫一個類,即可擁有任何類型的 vector;只需要寫一個函數,即可尋找任何類型 vector 中的最大值。例如:
vector<string>::iterator p = find(vs.begin(), vs.end(), "Grail");
vector<int>::iterator q = find(vi.begin(), vi.end(), 42);
這些例子出自標準模板庫(ISO C++ 標準庫中容器和算法那部分)。TC++PL 中,漫游標準庫那章對標準模板庫(Standard Template Library, STL)作了簡要的介紹。
GP 在某些方面比 OOP 要靈活得多。特別是,它不依賴于層級。例如,int 和 string 之間沒有任何層級關系。總的來說,GP 的結構化程度更甚于 OOP。事實上,GP 常被稱為“參數多態(parametric polymorphism)”;而 OOP 常被稱為“ad hoc 多態”。就 C++ 而言,GP 于編譯時就解析了所有名稱;它不需要任何動態(運行時)調度。因此,GP 在對運行時效率要求很高的領域占據了主導地位。
請注意,GP 并非萬靈丹。很多時候,程序并不需要參數化多態,而需要運行時調度(OOP)。
原文地址:http://www.research.att.com/~bs/bs_faq.html#generic
為什么 C++ 允許不安全的代碼?
也就是說,為什么 C++ 支持的一些操作能夠違反靜態(編譯時)類型安全機制?
- 為了直接訪問硬件(例如,把整數當作指向設備寄存器的指針)
- 為了獲取最佳的運行時效率和空間效率(例如,不檢測訪問數組元素的操作(是否越界),不檢測訪問對象的指針(是否有效))
- 為了和 C 兼容 </ul> 所以,當你并不需要以上三種特性時,最好避免有如瘟疫般的不安全代碼:
- 不要用類型轉換(cast),
- 不要將數組用作接口(如有必要,請將它們隱藏于高效函數和類的內部,而使用合適的 string、vector 等編寫其余代碼),
- 避免 void*(如果你真的需要它們,請將它們限制于低級 (low-level) 函數和數據結構的內部,并且為用戶提供類型安全的接口(通常是模版 (template))),
- 避免聯合體(union),
- 如果你對指針的有效性存有任何懷疑,請用智能指針(smart pointer),
- 不要“赤裸裸”地使用 new 和 delete(使用容器,資源句柄等),
- 不要使用 ...風格的函數(例如 printf)。 </ul> 幾乎所有 C++ 代碼都能遵循這些簡單的規則。請不要因為你編寫 C 代碼或者 C 風格的 C++ 代碼時不能遵循這些規則而認為 C++ 不安全。
- 語言本身不會有重大改變
- 標準庫會有重大擴展 </ul> </ul> 改變和擴展應該服務于使 C++ 成為一個更好的系統編程及庫建造平臺,同時也要使 C++ 更易于教學。毫無疑問,我們會對上面兩個句子中的“重要”二字的含義做一些討論。
- 歐洲計算機制造商協會 C++/CLI 標準。
- 英國 ISO C++ 小組的異議(包含一些代碼樣例)。
- ECMA 對英國 ISO C++ 小組(及其他人或組織)的異議的回復。
- Herb Sutter 寫的 C++/CLI 設計原理。 </ul> 原文地址:http://www.research.att.com/~bs/bs_faq.html#CppCLI
- B. Stroustrup: C and C++: Case Studies in Compatibility. The C/C++ Users Journal. September 2002. Pdf version.
- B. Stroustrup: C and C++: A Case for Compatibility. The C/C++ Users Journal. August 2002. Pdf version.
- B. Stroustrup: C and C++: Siblings. The C/C++ Users Journal. July 2002. Pdf version.
- B. Stroustrup: Sibling rivalry: C and C++. AT&T Labs - Research Technical Report. TD-54MQZY. January 2002. </ul> </ul> 我想象著不兼容性(通過同時對 C 和 C++ 進行修改)最終被消除掉后,仍然會存在 C 和 C++ 這兩種的語言,但屆時,C 就真的可以被稱之為 C++ 的子集了。
- “我認為我們應該從構建出來的應用程序中尋找優美之處,而不要在語言中尋找。”(我本應說“這勝于”,但卻說了“而不要”)
- “為了用好 C++,必須熟悉設計及編程技巧。”
- “C++ 是被設計來表達構想的,但假若你心中無墨或者不知如何表達,那 C++ 也無能為力。” </ul> </ul> 很多時候,“計算機科學”是數學狂熱的一種形式。是的,但請不要忽略“很多時候”這個詞。說這句話時的背景是:我正在擔心人們對數據的關注不足(經驗主義),而且對計算機科學和計算的連接也不夠關注。這并非對“計算機科學可以是科學”這種觀念的正面攻擊(以 Knuth 的算法及算法分析為例;那就是一門科學,并且不屬于任何其它科學)。呃,不,我不打算舉具體的例子或名字。
原文地址:http://www.research.att.com/~bs/bs_faq.html#unsafe
為了成為真正的 OO 程序員,在學 C++ 之前,我需要先學一門純 OO 語言嗎?
不。學新東西通常都是一個好主意。然而,每種語言都是不同的,并且帶有其獨有的風格和傾向。使用某些被認為是“純” OO 風格的語言編寫的代碼,總是不能很好地用其它語言模仿,而且,如果硬要逐字轉化成 C++ 的話,更是會碰個滿頭灰。而且,“編寫純 OO 代碼”也并非我的理想;請參考我的 OOPSLA 主題演講:為什么 C++ 不僅僅是面向對象的編程語言。如果你想成為一個優秀的 C++ 程序員,但卻抽不出多少時間,那么還是集中精力學習 C++ 和它所包含的觀念吧。
antigloss 注:OOPSLA 是 Object-Oriented Programming, Systems, Languages and Applications 的縮寫,意為面向對象編程、系統、語言,及應用程序。
原文地址:http://www.research.att.com/~bs/bs_faq.html#learn-pure
為什么 C++ 沒有圖形用戶接口?
C++ 有很多商用和開源的 GUI(Graphical User Interface,圖形用戶接口),例如 Gtkmm、SmartWin++、V C++ GUI、FLTK,以及 Qt。明確地說,不同平臺的提供商提供不同的 C++ 庫來訪問他們的 GUI。問題就在于沒有標準 GUI,而這正是主要問題所在。
請注意,提供 GUI 不單只是技術問題,它還涉及可行性問題。現在有很多 GUI,而且都有其用戶群,他們不會愿意別的 GUI 被聲明為標準。無論如何,標準委員會沒有足夠的資源建立一個更好的新 GUI。
原文地址:http://www.research.att.com/~bs/bs_faq.html#gui
為什么 C++ 不支持線程?
ISO C++ 標準沒有提及并發(concurrency),但所有主要的 C++ 實作都支持線程;pthread 幾乎無所不在,而且很多平臺提供商都提供他們自己的系統線程。出于可移植性的考慮,可使用一些線程庫,例如 Boost 線程庫。C++0x 幾乎肯定會包含一個線程庫。
原文地址:http://www.research.att.com/~bs/bs_faq.html#threads
C++0x 會是什么樣的?
我也不完全清楚。C++0x 將會是持續數年的標準化進程的成果。在這個進程中,會認真考慮 C++ 社群各個不同派別的需要——關于發展方向的討論正在進行中。C++ 標準將長期保持穩定,而 C++ 語言更是會保持更長期的穩定,因為兼容性始終是首要考慮的問題。
我個人認為,主要的原則應該是:
我在 SD2001w 的一個座談會上簡要地表達了我的一些觀點,在 2002 年春季的 ACCU(C 和 C++ 用戶協會)會議的主題演講中稍微細節一點地闡述了這些觀點。請翻閱我設計 C++0x 的首要準則以及我的發布頁中的其它新近論文。WG21 上有一個更完整的提案列表。我的 C++ 頁面中有更多關于 C++0x 標準及其冀望列表(wish list)的信息。
C++0x 中的 'x' 被冀望為 9:C++09,而不是(例如)C++0xA(十六進制)。:-)
原文地址:http://www.research.att.com/~bs/bs_faq.html#When-next-standard
何時會有新的 ARM ?
Ellis 和 Stroustrup 合著的《帶評注的 C++ 參考手冊》(The Annotated C++ Reference Manual, ARM),俗稱“The ARM”,寫于 1989 年。它曾是 C++ 標準化工作的基礎,但現在它已經太老了,不能反映當前的 C++ 標準。
由于種種原因,原計劃的描述 ISO C++ 標準的替代品(“ARM++”)沒能寫成。又因為關于 C++0x 標準的工作已經開始,所以現在寫一本基于 ISO C++ 1998 的 ARM++ 已經太晚了。
原文地址:http://www.research.att.com/~bs/bs_faq.html#ARM
你怎么看C#?
我對C#沒有什么評論。你很難說服我為什么世界上需要另一個專有語言,更難說服我為什么還需要一個和特定的專有操作系統結合得那么緊的語言。
顯然,我不是專有語言的愛好者,而是開放的正式的標準的愛好者。
-------------------------------------- 分割線 --------------------------------------如果你想專門為 .Net 平臺編程,C# 并非最差的選擇。記住,.Net 也強烈支持 C++(盡管宣傳得不那么多)。
(這句話是 B.S. 新增的,舊的譯文中沒有)
原文地址:http://www.research.att.com/~bs/bs_faq.html#Csharp
您怎么看待 C++/CLI?
C++/CLI 是對 ISO C++ 的一組擴展,它將 C++ 和微軟的 CLI(通用語言架構 Common Language Infrastructure)極其完整地“捆綁”在一起。它已經通過 ECMA 進行了標準化(ECMA-372)。我很高興它(C++/CLI)使得 C++ 可以很容易地使用 CLI 的所有特性,也為它遠勝于它的前任“托管 C++(Managed C++)”感到欣喜。然而,C++/CLI 為了達到這些目的,卻對 C++ 進行了各種語言特性上的擴展,以迎合 CLI 的各種特性(接口、屬性、泛型、指針、繼承、枚舉等很多很多),我對此深表遺憾。這將成為混亂的主要源頭。C++/CLI 比 ISO C++ 多出了大量新的語言上的便利,這會誘導程序員(不知不覺地)編寫出不可移植的代碼,而且這些代碼和 MS Windows 卻有著親密接觸。
CLI 提供了一套訪問系統設施(facilities)和應用程序的接口,而這套接口卻和傳統的接口大相徑庭。特別是,這套接口的語義不能完全或者便利地用傳統的編程語言來表達。一種形容 CLI 的方法是把它當作(局部的)“平臺”或者“虛擬機”。它包含一套龐大的語言特性(繼承、方法、循環構造 (loop constructs),回調機制 (callback mechanisms) 等),支持一套龐大的基礎類庫(即 BCL),還有一個精巧的元數據(metadata)系統。CLI 有時也被稱為“語言中立者”。然而,如果一種語言不大量引入 CLI 的特性,它甚至不能使用 .NET 的基本設施(或者 MS Windows 將要添加的設施,如果 MS 的計劃不變的話)。而且,如果一種語言不能表達所有這些特性,那它就不能應用于這個實作(.NET),因為 .NET 的資源是為其它(支持這些特性的)語言服務的。因此,CLI 是“語言中立者”這種說法是僅僅建立在所有語言為了成為 .NET 上的“一流”,都必須支持 CLI 的所有特性這個基礎上的。
我更希望綁定是通過少數原語(primitive)來完成的,而且這些原語應該能被任何編程語言用簡單的函數調用和數據結構來表示,或者把這些原語封裝于語言特有的庫里。而 CLI 最多只對全盤接受 CLI 設施的語言提供這種支持。為了生成 CLI 模塊,編程語言必須能夠表達 CLI 的全部特性,包括原數據。只有能做到這些的編程語言才能成為 .NET 的系統編程語言。進而,MS C++ 小組斷定,只有內建語言特性才能被用戶接受。他們的設計反映出來的思維是:帶 C++/CLI 擴展的 C++ 可以使用 CLI 的任何設施;使用 CLI 設施時,(C++/CLI)比其它語言更加簡練;和其它語言相比,(C++/CLI)絕無多余花銷。他們的目標是使 C++ 成為主流的 Windows 系統編程語言。
一如既往,我極力強調可移植性,并且建議程序員在設計應用程序時,通過 ISO C++ 中明確定義的那些接口訪問系統特定的設施(例如不要直接使用 C++/CLI)。在 Windows 上,這樣做有時會比直接使用 C++/CLI 的設施來的麻煩,但這是獲取可移植性和降低(平臺)銷售商依賴度的唯一途徑。當然,如果一段代碼的目的是為其它代碼提供 CLI 接口,那么這種間接使用 CLI 的方法也是難以維護的。請注意,我知道系統特定擴展的必要性,我也知道提供這種擴展的 C++ 銷售商并非只有微軟,我只是強烈希望這種擴展能通過 ISO C++ 允許的“小巧的接口”來實現。
如何處理系統特定的擴展是一個固有的難題。微軟 C++ 小組,特別是 Herb Sutter,一直都在和 ISO C++ 標準委員會的其他成員討論這個問題,以期最終理清 ISO C++ 及其超集 C++/CLI 的關系。在 ISO C++ 標準委員會,我們已經作出了許多有建設性的合作。同時,為了避免混淆 ISO C++ 和 C++/CLI 擴展,微軟正在修改 VC++ 文檔,以期清楚地區分 ISO C++ 和 C++/CLI(單純寫 C++ 即表示 ISO C++)。我希望其他銷售商也會跟隨這種潮流。
如何稱呼 C++ 的 CLI 綁定/擴展是一個充滿爭議的難題,我個人傾向于使用 C++/CLI 作為“ISO C++ 的 CLI 擴展”的縮寫。在名稱中保留 C++ 可以提醒人們(C++/CLI 的)基礎語言是什么,并且有助于保持 C++ 為帶 C++/CLI 擴展的 C++ 的嚴格子集。C/C++ 兼容性問題論證了保持子集嚴格性的重要性。
以下是一些關于C++/CLI的文檔:
您如何看待 EC++ ?
EC++(基本上)是 C++ 去掉異常、模板、命名空間、RTTI 支持、多繼承等特性后的一個子集。定義這個子集的是一個“工業聯盟”。我不喜歡語言子集或者方言,尤其是那些不支持標準庫的子集,因為這樣的話,該子集的用戶就不得不各自開發不兼容的基礎庫。我擔心定義 C++ 子集會造成用戶群體分裂,并引起相互間攻擊(1999-3-31:我看到一個廣告用生動的圖像來顯示 EC++ 如何通過廢除命名空間、模板、C++ 標準字符串類,以及其它東西來減“肥”(例如內存空間)。嘆息)。我強烈希望關于“標準”的工作發生于開放的研討會(例如 ISO 或者國家標準組織)。
ISO C++ 委員會的性能報告里有一個關于嵌入式系統實現者如何通過標準 C++(比用方言好)解決性能問題的討論。就我所知,EC++ 已經死了(2004),而如果它還沒死的話,也行將就木。
想了解 ISO C++ 如何應用于苛刻的嵌入式系統編程,可參考 JSF 航空器 C++ 代碼標準。
原文地址:http://www.research.att.com/~bs/bs_faq.html#EC++
為何您如此看重可移植性?
成功的軟件都是長壽的;數十年生命周期的軟件并不少見。針對某種硬件、操作系統、數據庫系統等設計的應用程序/程序,如果設計得當,其壽命通常都比這些東西還要長。通常,一個優秀的軟件的壽命會比提供構造這個軟件的基本技術的公司的壽命還要長。
通常,一個成功的應用程序/程序的客戶/用戶使用的平臺會有所不同。而且不同平臺的數目會隨著用戶數目的增加而增加。如果某個應用程序/程序緊密依附于一個單一的平臺或者(平臺)銷售商,那么它就會丟失很多潛在的用戶。
顯而易見,完全平臺無關性意味著不能使用平臺特定設施。然而,通過“小巧的接口”把應用程序眼里的環境包裝成庫,然后通過這些接口訪問平臺設施,這樣往往就可以構造出近似平臺無關的應用程序。
原文地址:http://www.research.att.com/~bs/bs_faq.html#portability
C 是 C++ 的子集嗎?
嚴格按照數學上的定義來說,C 不是 C++ 的子集。有些程序在 C 里面是合法的,但在 C++ 里卻是不合法的;甚至有些編寫代碼的方式在 C 和 C++ 中有不同的含義。然而,C++ 支持 C 所支持的全部編程技巧。任何 C 程序都能被 C++ 用基本相同的方法寫出來,并且運行效率和空間效率都一樣。把數萬行 ANSI C 代碼轉換成 C 風格的 C++ 代碼,通常只需要幾個小時。因此,C++ 是 ANSI C 的超集程度和 ANSI C 是 K&R C 的超集程度以及 ISO C++ 是 1985 年的 C++ 的超集程度差不多。
編寫風格好的 C 程序通常會是合法的 C++ 程序。例如,Kernighan 和 Ritchie 合著的《C 程序設計語言(第二版)》中的所有例子都是 C++ 程序。
C/C++ 兼容性問題的一些例子:
int main()
{
double sq2 = sqrt(2); /* 不是 C++:調用了未經聲明的函數 */
int s = sizeof('a'); /* 隱蔽的區別:C++ 中是 1,而 C 中卻是 sizeof(int) */
}
調用未經聲明的函數在 C 里是不良風格,而在 C++ 里是非法的。同樣的情況還有,傳遞參數給一個沒有在其聲明中列出參數類型的函數:
void f(); /* 沒有注明參數類型 */
void g()
{
f(2); /* C 中是不良風格。C++ 中不合法 */
}
C 里面,void* 可以隱式轉換為任何指針類型,并且堆空間分配通常是通過 malloc() 進行的,無法檢查是否已經分配了足夠的內存:
void* malloc(size_t);
void f(int n)
{
int* p = malloc(n*sizeof(char)); /* C++ 中非法。C++ 使用 new 分配堆空間 */
char c;
void* pv = &c;
int* pi = pv; /* 隱式轉換 void* 為 int*。C++ 中非法 */
}
請注意,隱式轉換 void* 為 int* 引起了潛在的數據對齊錯誤。請參閱 C++ 中 void* 和 malloc() 的代替品。
把 C 代碼轉換成 C++ 時,請緊記 C++ 的關鍵字比 C 多:
int class = 2; /* C 中合法。C++ 中是語法錯誤 */
int virtual = 3; /* C 中合法。C++ 中是語法錯誤 */
除了少數例外,如上面所述的例子(以及 C++ 標準和 C++ 程序設計語言第三版附錄 B 里詳細列舉的例子),C++ 是 C 的超集。(附錄 B 可以通過下載獲取)
請注意,“C”在以上段落中指的是經典 C 和 C89。C++ 不是 C99 的后裔;C++ 和 C99 是兄弟。C99 引入了一些新特性,造成 C/C++ 的不兼容性進一步增大。這里有一篇 C++98 和 C99 的不同點的描述。
原文地址:http://www.research.att.com/~bs/bs_faq.html#C-is-subset
您真的認為 C 和 C++ 可以合并為同一種語言嗎?
我認為如果它們能合并為同一種語言,對 C/C++ 社群(community)來說實在是再好不過了。也就是說,如果能夠系統且完全地消除 C/C++ 的不兼容性,并且有組織地防止它們未來的發展產生新的不兼容性,這就太好了。可能與否那就是另外一回事了。
我的基本觀點是,C/C++ 目前的不兼容性源于“歷史的偶然”,而非什么特殊的原因,盡管對于某些能人善士來說,它們“在那時看來都是非常好的主意”。 C/C++ 的不兼容性對 C/C++ 社群普遍沒有什么好處,反而對社群中的大部分人造成不少嚴重麻煩,所以應該通過不懈的努力消除它們的不兼容性。
欲了解我對 C/C++ 兼容性的看法的更詳細的陳述,請參考我就這個問題寫的一系列論文:
請注意,這些論文都是 2001 年末到 2002 年初之間寫的,那時仍然能夠看到一個希望,就是 C 和 C++ 標準委員會相互協作,最終在十年內制定出一個可行的方案。然而,這并沒有發生。
原文地址:http://www.research.att.com/~bs/bs_faq.html#merge
您如何看待 C/C++ ?
不,這并非我常被問到的問題。也就是說,這個問題是這份 FAQ 中的一個“假的 FAQ”。然而,這個問題應該成為一個 FAQ,因為人們在使用“C/C++”時,似乎以為“C/C++”是指某種特定的東西,并且以為他們知道“C/C++”所指的是什么,這導致了很多混淆和苦惱。人們應該詢問“什么是 C/C++ ?”,然后深思熟慮并且停止使用這個術語。因為使用這個術語真的會帶來不良影響。
沒有任何語言叫“C/C++”。使用這個詞的人通常并不了解編程(例如人事和經理)。也就是說,一些根本不懂 C++(并且常常也不懂 C)的人會使用這個詞。當程序員使用這個詞的時候,常常帶有一種這樣的態度:“C++ 就是增加了一大堆復雜而弊大于利的特性的 C”。這類人往往對標準庫中 printf 和 memcpy 以外的東西知之甚少,喜歡編寫他們自己的字符串類和哈希表。還有一些人因為一些無懈可擊的原因而堅持僅用 C++ 中的一個特定子集,但他們(就我所知)并不說“C/C++”。
我個人只在形如“C/C++ 兼容性”之類的詞語中使用 C/C++。
原文地址:http://www.research.att.com/~bs/bs_faq.html#C-slash
為何編譯 C++ 版的“Hello World”程序生成的代碼比 C 版的多十倍?
在我的機器上并非如此,你的也不應該這樣。我甚至曾見過 C++ 版的“Hello world”程序比 C 版的小。最近(2004),我在 Unix 下用 gcc -o2 進行測試,發現兩個版本(iostream 和 stdio)的大小完全相同。沒有語言層面上的原因會使一個版本比另一個大。這都取決于(編譯器的)實現者如何組織庫。如果一個版本明顯比另一個大得多,那就向產生更多代碼的(編譯器的)實現者報告這個問題。
原文地址:http://www.research.att.com/~bs/bs_faq.html#Hello-world
為何您把 C++ 設計得和 C(基本)兼容?
我想讓 C++ 兼容一門完整的語言,并使其擁有足以應對乃至無比苛刻的系統編程的性能和靈活性。我懼怕創造出又一種雖然優美,卻隱含著一些無心的限制的語言。《C++ 語言的設計和演化》章節 2.7 中有詳細的歷史細節。“您真的認為……?”中列舉的那些文章回顧了 C/C++ 兼容性問題技術上的討論。
那時,我認為 C 是可供使用的最優秀的系統編程語言。雖然當時(1979)這(C 的優秀)并不像后來那樣顯而易見,但是我可以請教 Dennis Ritchie、Steve Johnson、Sandy Fraser、Greg Chesson、Doug McIlroy,以及 Brian Kernighan 等專家并得到他們的反饋。如果沒有他們的幫助和建議,也沒有 C,C++ 極有可能胎死腹中。
與謠傳相反的是,從來沒有人跟我說要我必須使用 C;也沒有人跟我說不要使用 C。事實上,第一版 C++ 參考手冊便是基于 Dennis 給我的 C 參考手冊的 troff 版本完成的。很多新語言都是貝爾實驗室設計的;至少在“研究階段”,沒有制定加劇語言偏見的規則。
原文地址:http://www.research.att.com/~bs/bs_faq.html#whyC
C++ 歸您所有嗎?
不。如果非要說“C++ 歸誰所有”的話,那只能是 ISO。AT&T 將我以前寫的 C++ 參考手冊的所有權授予給了 ISO。ISO C++ 標準的版權歸 ISO 所有。
C++ 編譯器銷售商不用支付版稅給我和 AT&T。ISO C++ 標準是一份旨在讓所有人免費使用的規范文檔(當然,這份文檔本身是需要付錢向 ISO 或者國家標準委員會購買的)。不同的編譯器歸不同的銷售商/提供商所有。
“但有 SCO 的人聲稱 C++ 歸他們所有”;難道此言不實? - 這完全是無稽之談。我看過那次訪談。那個 SCO 的家伙肯定完全不知 C++ 為何物,竟將之稱為“the C++ languages”[Antigloss 注:請注意這里使用了 language 的復數]。SCO 頂多擁有已有 15 年歷史并早已嚴重過時的 Cfront - 我最初設計的 C++ 編譯器。我過去小心地不讓 C++ 和任何專利或商標沾上關系。這是我們書寫“C++”而非“C++(tm)”的原因之一。C++ 標準不受任何專利限制 - 標準委員會也對此進行過核實。
原文地址:http://www.research.att.com/~bs/bs_faq.html#revenues
“C++”何得此名?
《C++ 程序設計語言》(TC++PL)第一章:“C++(讀作‘斯加加’)這個名字是 Rick Mascitti 于 1983 年夏天起的。這個名字能充分表明 C++ 是由 C 演變而成的這一進化本質;‘++’是 C 的自增運算符。‘C+’這個稍短的名字是個語法錯誤;它亦已被用作一種不相關的語言的名字。精通 C 的語義的大師認為 C++ 這個名字比不上 ++C。這門語言沒有被命名為 D 是因為它是 C 的擴展,而且它并沒有試圖通過刪減 C 的特性來修正 C 存在的一些問題。想了解 C++ 這個名字的另一種解釋,可翻閱 [Orwell,1949] 的附錄。”
《C++語言的設計和演化》(D&E)第三章:“我采用 C++ 這個名字,因為它比較短,很有含義,而且也不是‘什么什么的 C’這種形式的名字。C 語言中,++ 可以根據上下文讀成‘next’、‘successor’或者‘increment’,不過通常還是讀成“加加”。C++ 這個名字及其競爭對手 ++C 是產生笑話和雙關語的肥沃土壤——幾乎所有這些笑話和雙關語在 C++ 這個名字被采用之前就已為人們所熟知和品味。C++ 這個名字是 Rick Mascitti 建議的。1983 年 12 月,這個名字第一次被使用——它被寫入了 [Stroustrup,1984] 和 [Stroustrup,1984c] 的最終版。
C++ 中‘C’這個字母擁有悠久的歷史。顯然,它是 Dennis Ritchie 設計的語言的名字。C 的直接祖先是 Ken Thompson 設計的叫做 B 的解釋語言,而 B 語言是 BCPL 的后裔。BCPL 是劍橋大學的 Martin Richards 在訪問位于另一個劍橋(坎布里奇)的 MIT 時設計和實現的。BCPL 代表 Basic CPL,而 CPL 是一門相當大(就當時而言)而優雅的編程語言的名字,它是劍橋大學和倫敦大學合力開發出來的。在倫敦大學參與進來之前,CPL 中的‘C’代表劍橋。后來,‘C’的正式含義是組合,而它的非正式含義是 Christopher,因為 Christopher Strachey 是主持 CPL 開發的首腦。”
原文地址:http://www.research.att.com/~bs/bs_faq.html#name
您是使用何種語言編寫出 C++ 的呢?
第一個 C++ 編譯器(Cfront)是使用 C++ 編寫出來的。為此,我首先使用 C 編寫了一個將“帶類的 C”轉換成 C 的預處理器。“帶類的 C”屬于 C 的一種變種,它是 C++ 的直接祖先。這個預處理器將“帶類的 C”的結構成分(例如類和構造函數)翻譯成 C。它是一個傳統的預處理器,不能完全理解 C++ 語言,還將大多數類型檢查留給了 C 編譯器來做,而且其對 C++ 成分結構的翻譯是單個單個地進行的,而不會先對全局進行分析。然后我利用“帶類的 C”編寫出了 Cfront 的第一個版本。
Cfront 是一個傳統的編譯器,它會對 C++ 源代碼進行完整的語法和語義檢查。為此,它要有一個完整的詞法分析器,要建立符號表,還要為每個類、函數等建立一套完整的內部樹狀表示。在生成 C 代碼之前,它還會對 C++ 成分結構的內部樹狀表示做一些源代碼層面上的優化。雖然這個版本(的 C++ 編譯器)生成 C 代碼,但它不依賴 C 做任何類型檢查。它僅僅把 C 用作匯編器。最終生成的代碼非常快。想知道更多信息,可參考 D&E 。
原文地址:http://www.research.att.com/~bs/bs_faq.html#bootstrapping
為何 C++ 如此龐大?
C++ 并非有些人想像的那么龐大。它并非為教學目的而設計的小巧語言,但那些人們經常用來和 C++ 比較的語言也不小巧,例如 C、Java,C#。和 Wirth 博士最初定義的 Pascal 相比較,它們也非常龐大。當然,這樣設計是有合理的理由的,我這么認為。和 30 年前相比,當今的編程世界已經變得異常復雜,現代編程語言恰恰反映了這一點。
C++ 標準共有 740 頁,但其中有 400 頁描述標準庫。語言特性的(極度詳細的)描述只不過占了 340 頁。類似地,TC++PL 共有一千余頁,但僅有 350 頁用于解釋語言特性及其用法;其余部分討論標準庫、編程技巧等。
C++ 直接支持(也就是內建于語言中)一些其它語言通過庫來支持的特性,所以 C++ 的語言部分會相對更龐大一些。另一方面,如果你想編寫一個“典型的現代應用程序”,那你還需要考慮操作系統接口、GUI、數據庫,網頁接口等。你必須熟悉的東西包括語言特性、庫,以及編程習俗與標準。和這些東西的總和相比,編程語言本身還是很小的。C++ 的龐大可以說是一個優點,因為它能更好地支持優秀的庫。
最后,編程新手也能了解一門語言的全部特性的時代已經一去不復返了,至少對于那些廣泛應用于工業上的語言來說是這樣。只有少數專家級的人了解“C 的全部”或者“Jave 的全部”。由此推之,沒有人需要為新手不了解 C++ 的全部而道歉。你必須做的是——無論學任何語言——先學其中一個子集,開始編寫代碼,然后循序漸進地深入學習這門語言,它的庫及其開發工具。
原文地址:http://www.research.att.com/~bs/bs_faq.html#big
現在還有人使用 C++ 嗎?
當然,有很多。C++ 的用戶雖然多得難以統計,但計量單位肯定是百萬級的。所有主要的平臺銷售商都支持 C++。我的應用程序例舉中有一些 C++ 的應用例子。
原文地址:http://www.research.att.com/~bs/bs_faq.html#use-C++
為何 C++ 沒被用于編寫操作系統?
它已被用于編寫操作系統有些年頭了(超過十年);請參考我整理的 C++ 應用程序列表。
原文地址:http://www.research.att.com/~bs/bs_faq.html#use-C++-for-OS
有什么好的認證是面向 C++ 程序員的嗎?
就我所知,沒有什么好的面向 C++ 程序員的認證。很遺憾。一個好的認證是最有用的。然而,C++ 缺乏提供可靠認證的中央機構,而且,一個不權威的認證或者專注于語法的認證毫無用處。
原文地址:http://www.research.att.com/~bs/bs_faq.html#certification
為什么你不回復我的電子郵件?
我會回信,但是我收到的信太多了。我估計我回了95%以上的信。但是有時候我會被信件淹沒。有些信丟失了,有些要等到有空的時候才回,有些則要等到我回復一批相關信件的時候(經常是對我的書中潛在錯誤的評論)。不幸的是,越長、越有內涵的信,往往會比問題簡單答案也簡單的信拖得更久。
還有,如果你寫信給我,請確認我能夠給你回信。我很討厭在我寫完并且發出回信后,發現回信地址無效或者無法訪問。這種事情每周都會發生。
相對來說,這樣兩種來信被忽略的可能性比較大:關于家庭作業的問題和類似“我怎么使用某某庫?”的問題。對于不回答后一個問題,我感到有點遺憾,因為提問者經常不明白DOS、Windows或者其他任何C++編程接口并不是C++標準的一部分(并且我也不可能跟上這么多的C++庫的發展)。如果你沒有收到回信,那么請先考慮一下你的問題是不是上面這幾種類型。
Antigloss 注:以下一段是我補譯的。因為這段是 B.S 新增的,原來的中文翻譯中沒有。
此外,除非你注明你的名字,否則我不會讀你的郵件,并且會將其刪掉。這是一個新政策。我從來就不喜歡假名。我發現和那種自認為使用類似 suuupergeeek 或者 coolGuy3 這種名字很酷的人,很難進行禮貌的技術交流,所以我不想費心嘗試和這種人交談。
原文地址:http://www.research.att.com/~bs/bs_faq.html#email
“bjarne”是冒名頂替的嗎?
或許不是。大多數新聞組帖子、訪談等,如果聲稱是我的,那的確是我的。不過,一個顯而易見的例外是那篇聲名狼藉的 IEEE “訪談”。在我看來,這篇“訪談”非常無聊。我用 gmail 賬號在新聞組上發帖,使用的用戶名是“Bjarne”,這是為人們所熟知的。可不知何故,有些人好像被它搞糊涂了。如果你有所懷疑,請思量那份可疑信息的風格和內容,并且和論壇中(我的)其它帖子比較,或者發問。
原文地址:http://www.research.att.com/~bs/bs_faq.html#impostor
那真是你說的嗎?
是的,我是說過“C容易讓你開槍時不小心打到自己的腳,C++雖然不這么容易,但是你如果真這么做了,它會把你整條腿轟掉”。但人們往往會忽視,我對C++所說的話在不同程度上對于任何強大的語言都是正確的。當你防止人們犯簡單的錯誤時,他們就會開始犯新的、不那么明顯的錯誤。那些能避免簡單錯誤的人,可能會直奔不那么簡單的錯誤而去。對于堅固的保護性的環境,有一個問題,就是難題總是被太晚發現,以至于一旦發現就難以補救。并且,罕見的問題比常見問題更難發覺,因為你往往不會懷疑到它。
我也說過“在C++里面存在著一個更小一些和更清晰一些的語言,它正在掙扎著浮現出來”。比如,原文在The Design and Evolution of C++的207頁(譯注:中文版156頁)就能找到。不過這個更小更清晰的語言不是Java或C#。原文是在名為“Beyond Files and Syntax”的小節里。那里我指的是C++的語義要比它的語法清晰得多。我是在考慮一種編程風格、程序庫和編程環境,相對關注于C的低級層面的古老用法而言,它們強調更清晰更有效的編程實踐。
----------------------------------- 分割線 -----------------------------------“我一直希望我的電腦可以像我的電話那么易用;我的愿望已經實現了,因為我已經不知道怎么使用我的電話了”。大約在 1990 年,在嘗試使用一個“功能強大”的電話受挫后,我說了這句話。我敢肯定這種心情并非為我獨有,甚至這句話也可能并非我的原創;肯定有人在我之前就已經這么想了。
“只有兩種語言:一種被人抱怨,而另一種沒人使用”。是的。同樣,我也非常懷疑這種心情是否為我獨有。當然,必須持懷疑態度對待所有“只有兩種……”的引證。
“用類推來證明其實是欺騙行為”。是的;TC++PL 第 692 頁。優秀的類推是闡述觀點的極好的途徑,但很多時候,這些類推卻沒有可靠的論據,數據等。
“自以為是的人總會讓自知的人覺得厭煩”。是的。
“C++ 是我最鐘愛的垃圾回收語言,因為它幾乎不產生垃圾”。是的;請參考為什么 C++ 沒有垃圾回收機制?以及我如何處理內存泄漏。
“如果你認為這很簡單,那么你已經誤解了這個問題”。是的,但我不記得這句話是回答哪個問題的了。
“使用被視為很糟糕的語言開發出來的系統有很多,比那些用滿載贊譽的美麗語言開發出來的系統要多得多。” 是的,在 MIT 技術回顧訪談中和別的地方。我同時也說了:
很多時候,“軟件工程”既不是工程學也不涉及軟件。是的,但請不要忽略“很多時候”這個詞。說這句話時的背景是:我正在擔心人們對數據的關注不足(經驗主義),而且對軟件工程和代碼的連接也不夠關注。我擔心必須交付有用的可維護代碼這個事實可能會淹沒于一系列步驟、共同標準和市場營銷中;我也擔心軟件開發有時會受控于一些不能分辨代碼好壞并以此為豪的人。呃,不,我不打算舉具體的例子或名字。假若實踐得當,軟件開發會是一門有價值的工程學科,它會使工程原理推陳出新。
Antigloss 注:分割線前的譯文乃葉豐翻譯的,而其后的譯文則是我翻譯的。
原文地址:http://www.research.att.com/~bs/bs_faq.html#really-say-that來自:http://www.stroustrup.com/bsfaqcn.html