C和C++有什麼不同之處?

  • 作者:由 匿名使用者 發表于 收藏
  • 2023-01-20

說詳細點。

C和C++有什麼不同之處?xhygm2014-03-05

————————

360問答

————-

買們

————————————-

————————-

——————————-

將標準 C+

容裡防乙井羅令取獲板

+ 視為一個新語言

Learning Standard C++ as a New Language

作者 Bjarne Stroustr

up

譯者 陳崴

就別再把 C++ 視為 C 的後一個語言了吧。這個問題問

華劑

C++ 之父就對了。

侯捷注:本文系北京《

程式設計師》雜誌 2001

/04 的文章。譯筆順暢,技術飽滿。

承譯者陳崴先生與《程式

員》雜誌負責人蔣

濤先生答允,

轉載於此,以饗臺灣讀者,非常感謝。

未得陳崴先生與蔣濤

先生二人之同意,任

何人請勿將此文再做轉載。

————————

由介體無衡引亂原

————-

————-

————————————

煙帝例歲跟受林備

————

————-

————-

C/

季絲勞料起曾節假以

C++ User‘s Journal May,1999

Learn

ing Standa

rd C++ as a New La

影調婷益常弱作考質即

nguage

by Bjarne Stroustr

up

——————-

——————-

————————

——————-

————————————————-

匯入

想要獲得標準 C++ [叄考資料 1] 的最大優點,我們必須重

新思考 C++ 程式的撰寫方

式。重新思考的方

式之一就是,想想 C+

+ 應該如何學習(和教育)。我們

應該強調什麼樣的程式設計技術?我們

應該先學習這個語言的哪一部份?在真正程式碼中我們想要突顯的又是哪一部份?

這篇文

章把幾個簡單的 C++ 程式拿

來比較,其中有些以現代化

第青真

風格(使用標準程式庫)撰寫,有些以傳統的 C 語言風格撰寫。

速突亮雜程室阻底車貴

從這些簡單例子所學到的功課,對

合評雲原幹胞再推

大程式而言仍然具有重要意義。

大體而言,這篇文章

主張將 C++ 視為一個更高階的語言來使用,這個語言依賴抽象性提供簡練與優雅,但又不失

低階風格下的效率。

我們都希望程式容易撰寫,正確執行,易於維護,並且效

率可被接受。這意味我們應該

以最接近此一理想的方式

來使用 C++(或任何其他語言)

。我猜想 C++ 族群尚未能

談稱間執

夠消化標準 C++ 所帶來的

種種設施;重新思考我們對 C++

的使用方式,可獲得一

些重要的改善並進而達

到上述理想。本文所

重視的程式設計風格,焦點在於充

份運用標準 C++ 所支援的設施,而不在那些設施本身。

主要的

出貴露

改善關鍵就是,透過對程式庫

境乙怕哪滑兩身受混身

的運用,降低我們所寫的碼的

藥鐵拿多

大小和複雜度。以下我要在一些簡單例子中,示

文向斤答吸巴督夜劃七

範並量化這些降低程度。這類簡單例項可能出

話需曾徑

現在任何 C++ 匯入性課程中

益武職繼字屬

由於降低了大小和複雜度,我們也就減少了開發時間,減輕了維護成本,並且降低了測試成本。另一個重點是,透過程式庫的運用,我們還可以簡化 C++ 的學習。對於小型程式以及只求獲得好成績的學生而言,這樣的簡化應該是相當充裕的了。然而專業程式員對效率極為要求,只有在效率不被犧牲的情況下,我們才能期許自己提升程式設計風格,以滿足現代化服務和商務之於資料和即時迴應的嚴格需求。為此我展示了一份量測結果,證明覆雜度的降低並不會損失效率。最後我還討論了這種觀點對於學習和教育 C++ 所帶來的影響。

複雜度 Complexity

試考慮一個題目,它很適合做為程式語言課程的第二道練習(譯註:第一道練習當然是 “hello world” 啦):

輸出一個提示句 “Please enter your name”

讀入名字

輸出“Hello

在標準 C++ 中,明顯的解答是:

#include // 取得標準 I/O 設施

#include // 取得標準 string 設施

int main()

{

// 獲得對標準程式庫的取用權利

using namespace std;

cout << “Please enter your name: \n”;

string name;

cin >> name;

cout << “Hello” << name << ’\n‘;

}

對一個真正的初學者,我們必須解釋整個架構。什麼是 main()?#include 做了些什麼事?using 做什麼用?此外我們還得對所有的細瑣規榘有所瞭解,例如 \n 的意義,哪裡應該加上分號┅等等。

然而這個程式的主要成份,觀念非常簡單,和題目的文字敘述之間只是表示法的不同。我們必須學習這種表示法,那很簡單:string 就是一個 string(字串),cout 就是一個 output(輸出裝置),<< 就是一個我們用來寫到輸出裝置去的運運算元┅等等等。

為了進行比較,下面是傳統的 C-style 解法 [注 1]:

#include // 取得標準的 I/O 設施

int main()

{

const int max = 20; // name 的最大長度為 19

char name[max];

printf(“Please enter your name: \n”);

// 將字元讀入 name 之中

scanf( “%s” , name);

printf( “Hello %s\n” ,name);

return 0;

}

很明顯,主要邏輯有了輕微的 — 只是輕微的 — 改變,比 C++-style 版本複雜一些,因為我們必須解釋陣列和怪異符號 %s。主要的問題在於,這個簡單的 C-style 解答沒什麼價值。如果有人輸入一個長度大於 19 的名字(所謂 19,是上述指定的 20 再減 1 ,扣除的那個 1 用來放置 C-style 字串的結束字元),這個程式就完蛋了。

有人認為這種劣質品其實不會造成傷害,只要運用「稍後介紹」的某種適當解法。然而就算如此,引起爭議的那一行也只不過是「可接受」而已,還達不到「良好」的境界。理想上我們不應該讓一個菜鳥使用者面對一個容易當機的程式。

這個 C-style 程式如何才能夠像 C++-style 程式一樣地舉止合宜呢?首先我們可以適度運用 scanf 來避免陣列溢位(array overflow):

#include // 取得標準的 I/O 設施

int min()

{

const int max 20;

char name [max];

printf( “Please enter your first name: \n”);

scanf( “%19s”, name); // 讀入最多 19 個字元

printf( “Hello %s\n”, name);

return 0;

}

沒有什麼標準方法可以直接在 scanf 的格式字串中直接使用符號型式的緩衝區大小,所以我必須像上面那樣地使用整數字面常數。那是一種不良風格,也是日後維護時的一顆不定時炸彈。下面是專家級的作法,但實在難以對初學者啟口:

char fmt[10];

// 產生一個格式字串,如使用單純的 %s 可能會造成溢位(overflow)

sprintf(fmt, “%%%ds”, max-1);

// 讀入至多 max-1 個字元到 name 之中。

scanf(fmt, name);

猶有進者,這個程式會把 “超過緩衝區大小” 的字元砍掉。然而我們卻希望字串能隨著輸入而成長。為了做到這一點,我們必須將抽象性下降到一個較低層次,處理個別的字元:

#include

#include

#include

void quit()

{

// 寫出錯誤訊息並離開

fprintf( stderr, “memory exhausted\n”);

exit (1);

}

int main()

{

int max= 20;

// 配置緩衝區:

char* name = (char*) malloc(max);

if (name ==0) quit();

printf( “Please enter your first name: \n”);

// 跳過前導的空白字元

while (true) {

int c = getchar();

if (c = EOF) break; // 檔案結束

if (!isspace(c)) {

ungetc (c, stdin);

break;

}

}

int i = 0;

while (true) {

int c = getchar();

if (c == ’\n‘ || c == EOF) {

// 在尾端加上結束字元 0

name[i] = 0;

break;

}

name[i] = c;

if (i == max-1) { // 緩衝區填滿了

max = max+max;

name = (char*) realloc(name, max);

if (name == 0) quit();

}

itt;

}

printf( “Hello %s\n”, name);

free(name); // 釋放記憶體

return 0;

}

和先前的版本比較,這個版本明顯複雜得多。加上一段「跳過前導空白字元」的處理,使我感覺有些罪惡,因為我並未在題目敘述中明白提出這項需求。不過「跳過前導空白字元」是很正常的行為,稍後其他版本也都會這麼做。

可能有人認為這個例子並不是那麼糟糕。大部份有經驗的 C 程式員和 C++ 程式員在真正的應用程式中或許(順利的話)已經寫過某些這樣的東西。我們甚至可能認為,如果你寫不出那樣的程式,你就不能算是一個專業程式員。然而,想想這些東西加諸於初學者的觀念負擔吧。上面這個程式使用七個不同的 C 標準函式,在非常瑣屑的層次上處理字元層面的輸入,運用了指標,並自行處理自由空間(free store,譯註:通常即是 heap)。為了使用 realloc,我必須採用 malloc(而不是 new)。這把我們帶入了大小和型別轉換 [注 2] 的議題。在一個如此的小程式中,什麼才是處理可能發生之記憶體耗盡問題的最佳作法呢?答案並不明顯。這裡我只是做某些事情,以杜絕這個討論變質為另一個毫不相干的主題。慣用 C-style 作法的人必須謹慎地想想,哪一種作法對於更深一層的教學和最後的實際運用能夠形成良好的基礎。

總結來說,為了解決原本那個簡單問題,除了問題核心本身,我還得介紹迴圈,測試,儲存空間之大小,指標,轉型,以及自由空間之顯式管理。而且這種程式設計風格充滿了出錯的機會。感謝長久累積下來的經驗,我才能夠避免出現任何明顯的大小差一錯誤( off-by-one)或記憶體配置錯誤。我在面對 stream I/O 時,一開始也犯了典型的初學者錯誤:讀入一個 char(而不是一個 int)並忘記檢查 EOF。在 C++ 標準程式庫尚未出現的那個年代,一點也不令人驚訝,許多教師無法擺脫這些不值錢的東西,暫時擱置它們稍後再教。不幸的是,許多學生也僅僅注意到這種劣等風格 “夠好”,寫起來比其 C++ style 兄弟快。於是他們養成了一種很難打破的習慣並留下一條容易犯錯的軌跡。

最後那個 C-style 程式有 41 行,而功能相當的 C++-style 程式只有 10 行。扣除基本架構之後,比值是 30 : 4。更重要的是, C++-style 的那幾行不但較短,其本質也比較容易被瞭解。C++-style 和 C-style 兩種版本的行數及觀念複雜度很難客觀量測,但我認為 C++-style 版本有 10 : 1 的優勢。

效率 Efficiency

對一個無關痛癢如上述小例子的程式而言,效率算不上是個議題。面對這類程式,簡化和(型別)安全才是重點所在。然而,真正的系統往往由一些極重視效率的成份組成。對於這類系統,問題就變成了 “我們能夠給予較高階的抽象性嗎?”

考慮這類重視效率的程式,下面是個簡單的例子:

讀入未知數量的元素

對每個元素做某些動作

做某些涉及所有元素的動作

我能夠想到的最簡單而明確的例子就是,在程式中計算來自輸入裝置的一系列雙精度浮點數的平均值(mean)和中間值( median)。下面是傳統的 C-style 解法:

// C-style 解法:

#include

#include

// 一個比較函式,稍後將給 qsort() 使用。

int compare (const void* p, const void* q)

{

register double p0 = * (double* )p;

register double q0 = * (double*)q;

if (p0 > q0) return 1;

if (pO < qO) return -1;

return 0;

}

void quit()

{

fprintf(stderr, “memory exhausted\n”);

exit(1);

}

int main(int argc, char*argv[])

{

int res = 1000; // 最初的配置量

char* file = argv[2];

double* buf= (double*) malloc(sizeof(double) * res);

if (buf == 0) quit();

double median = 0;

double mean = 0;

int n = 0;

FILE* fin = fopen(file, “r”); // 開檔做為輸入用(reading)

double d;

while (fscanf(fin, “%lg”, &d) == 1) {

if(n == res) {

res += res;

buf = (double*) realloc(buf, sizeof(double) * res);

if (buf == 0) quit();

}

buf[n++] = d;

// 有 rounding errors 的傾向

mean = (n==1) ? d : mean+(d-mean)/n;

}

qsort(buf, n, sizeof(double), compare);

if (n) {

int mid=n/2;

median = (n%2) ? buf[mid] : (buf[mid-1]+buf[mid])/2;

}

printf( “number of elements=%d, median=%g, mean=%g\n”,

n, median, mean);

free(buf);

}

下面是常見的 C++ 解法:

// 使用 C++ 標準程式庫的解法:

#include

#include

#include

using namespace std;

main(int argc, char*argv[])

{

char* file = argv[2];

vector buf;

double median = 0;

double mean = 0;

fstream fin(file,ios::in);

double d;

while (fin >> d) {

buf。push_back(d);

mean = (buf。size() == 1) ?

d : mean+(d-mean)/buf。size();

}

sort(buf。begin(),buf。end());

if (buf。size()) {

int mid = buf。size() /2;

median =

(buf。size() % 2) ?

buf[mid] : (buf[mid-1] + buf[mid] )/2;

}

cout << “number of elements = ” << buf。size()

<< “, median = ” << median << “, mean = ”

<< mean << ’\n‘;

}

這兩個程式的大小,不再像前一個例子有那麼懸殊的差異(43 : 24,空行不計)。扣除無法刪減的共同元素,例如 main() 的宣告和中間值的計算(共 13 行),兩者的行數差異是 20 : 11。關鍵性的「輸入並儲存」迴圈和排序動作,在 C++-style 程式中都有顯著的縮短(「輸入並儲存」迴圈的行數差異是 9 : 4,排序動作的行數差異是 9 : 1)。更重要的是,在 C++ 版本中,每一行所包含的邏輯遠遠簡單得多 — 獲得正確性的機會當然也就多得多。

再一次,記憶體管理在 C++-style 程式中隱喻實施;當元素以 push_back 加入,vector 便自動成長。C-style 程式則是以 realloc 做記憶體顯式管理。出現在 C++-style 程式中的 vector 建構式和 push_back 函式會做掉 C-style 程式中的 malloc, realloc 動作,以及對於「被配置之記憶體大小」的追蹤動作。在 C++-style 程式中,我依賴異常處理(exception handling)來記錄記憶體的耗盡。在 C-style 程式中,我明白地測試以避免可能的記憶體耗盡問題。

一點也不令人驚訝,C++ 版本比較容易獲得正確。我以剪貼的方式從 C-style 版本產生出這個 C++-style 版本。我忘記含入;我留下了 n 而忘了使用 buf。size;此外,我的編譯器不支援局域( local)內的 using 指令,迫使我必須把它移到 main 之外。修正了這四個錯誤之後,程式就可以正確執行了。

對一個初學者而言,qsort 很是詭異。為什麼你必須給予元素個數?(因為陣列不知道它自己有多少個元素)為什麼你必須給予 double 的大小?(因為 qsort 不知道它要排序的單位是 doubles。)為什麼你必須寫那個醜陋的、用來比較 doubles 數值的函式?(因為 qsort 需要一個指標指向某個函式,因為它不知道它所要排序的元素型別)為什麼 qsort 所使用的比較函式接受的是 const void* 引數而不是 char* 引數?(因為 qsort 可以對非字串的數值排序)void* 是什麼意思?前面加上 const 又是什麼意思?(唔,稍後我們再來談這個話題)對初學者解釋這些問題,恐怕很難不使他們兩眼發直。相較之下解釋 sort(v。begin( ), v。end()) 就容易得多:「單純的 sort(v) 比較簡單,但有時候我們想要對容器的某一部份做排序,所以更一般化的方式就是指定排序運作範圍」。

為了比較效率,我首先必須決定多少筆輸入才能使效率的比較饒富意義。由於 50,000 筆資料也不過是用了此程式半秒鐘不到, 因此我選擇以 500,000 筆輸入和 5,000,000 筆輸入來做比較。結果顯示於表一。

表一 / 讀入、排序、輸出 浮點數

最佳化前 最佳化後

C++ C C/C++ 比值 C++ C C/C++ 比值

500,000 筆資料 3。5 6。1 1。74 2。5 5。1 2。04

5,000,000 筆資料 38。4 172。6 4。49 27。4 126。6 4。62

關鍵數字在於比值。比值大於 1 表示 C++-style 版本比較快。語言、程式庫、程式設計風格之間的比較,眾所周知十分棘手,所以請不要根據這些簡單的測試就做出徹底的結論。這些比值是不同機器上數次執行結果的平均值。同一個程式的不同執行環境,其間差異低於 1 個百分比。我也執行了我這個 C-style 程式的 ISO C 嚴格相容版本,一如預期,其間並沒有效率上的差異。

我預期 C++-style 程式會稍微快一點點。檢驗不同的 C++ 編譯器實作品後,我發現執行結果有著令人驚訝的變化。某些時候, C-style 版本在小資料量的情況下表現優於 C++- style 版本。然而本例的重點在於,我們可以面對目前已知的技術,提供一個較高階的抽象性和一個針對錯誤的較佳保護。我所使用的 C++ 編譯器既普遍又便宜 — 不是研究室裡的玩具。那些宣稱可以提供更高效率的編譯器,當然也適用本結果。

要找到一些人,願意在方便性和較佳的錯誤防範上面付出 3, 10 或甚至 50 的比值,倒也還不罕見。但如果把這些效益放在一起,再加上兩倍或四倍的速度,那就非常壯觀而吸引人了。這些數字應該是一個 C++ 程式庫供應商樂意接受的最小值。為了知道時間花在什麼地方,我又進行了一些額外測試(見表二)。

表二 / 讀入浮點數並排序。為了解輸入動作所耗費的成本,我加上一個 “generate” 函式,用來產生隨機數值。

500,000 筆資料:

最佳化前 最佳化後

C++ C C/C++ 比值 C++ C C/C++ 比值

讀入資料 read 2。1 2。8 1。33 2。0 2。8 1。4

產生資料 generate 0。6 0。3 0。5 0。4 0。3 0。75

讀入並排序 read & sort 3。5 6。1 1。75 2。5 5。1 2。04

產生並排序 generate & sort 2。0 3。5 1。75 0。9 2。6 2。89

5,000,000 筆資料:

最佳化前 最佳化後

C++ C C/C++ 比值 C++ C C/C++ 比值

讀入資料 read 21。5 29。1 1。35 21。3 28。6 1。34

產生資料 generate 7。2 4。1 0。57 5。2 3。6 0。69

讀入並排序 read & sort 38。4 172。6 4。49 27。4 126。6 4。62

產生並排序 generate & sort 24。4 147。1 6。03 11。3 100。6 8。9

當然,“read” 僅僅只是讀入資料,“read&sort” 僅僅只是讀入資料並排序,它們都不會產生任何輸出。為了對輸入成本獲得比較好的感覺,“generate” 用來產生隨機數值,而非從輸入裝置讀入資料。

在其他的例子和其他的編譯器身上,我料想 C++ stream I/O 會比 stdio 稍稍慢一些。本程式的前一版本使用 cin 而非 file stream,情況的確如此。在某些 C++ 編譯器上,檔案的 I/O 確實遠比 cin 快速得多,其理由至少有一部份是因為 cin 和 cout 之間的繫結的拙劣處理。然而,以上數值顯示,C++-style I/O 可以像 C-style I/O 一樣地有效率。

如果改變這些程式,使它們讀入並排序的物件是整數而非浮點數,並不會改變相對效率 — 雖然我們可以驚喜地發現,這種改變對 C++-style 程式而言實在非常簡單(只需兩個改變,C-style 程式需要 12 個改變)。這對於易維護性是一個好兆頭。 “generate” 測試所呈現的差異顯示出配置所花費的成本。一個 vector 加上 push_back 應該就像一個陣列加上 malloc/free 一樣快,但實際卻非如此。其原因是難以在最佳化過程中將「什麼事都沒做的初值設定列( initializers)」的呼叫動作去除。幸運的是,配置所引發的成本,在輸入(造成配置需求)所引發的成本面前,幾乎總是顯得渺小。至於 sort,一如預期遠比 qsort 快得多,主要原因是 sort 內的比較動作是行內展開(inlines),而 qsort 必須呼叫某個函式。

實在很難選擇一個例子可以好好說明效率議題。我從同事身上獲得的意見是,讀入並比較「數值」還不夠寫實,應該讀入「字串」並排序。所以我寫了以下程式:

#include

#include

#include

#include

using namespace std;

int main(int argc, char* argv[])

{

char* file = argv[2]; // 輸入檔的檔名

char* ofile = argv[3]; // 輸出檔的檔名

vector buf;

fstream fin (file,ios::in);

string d;

while (getline (fin, d))

buf。push_back (d);

sort(buf。begin(), buf。end());

fstream fout (ofile, ios: out);

copy(buf。begin(), buf。end(),

ostream_iterator (fout, “\n”));

}

我把它改寫為 C 的型式,並設法讓字元的讀入得以最佳化。C++-style 版本執行得很好 — 即使是面對經過手動調整而達到最佳化效果的 C-style 版本(後者消除了字串的複製動作)。對於小量輸出而言,沒有什麼顯著差異,但對於大量資料而言,sort 再一次擊敗了 qsort,因為其較佳的行內展開(inlines),見表三。

表三 / 讀入、排序、輸出 字串

C++ C C/C++

比值 C,去除

字串複製動作 最佳化後的

C/C++ 比值

500,000 筆資料 8。4 9。5 1。13 8。3 0。99

2,000,000 筆資料 37。4 81。3 2。17 76。1 2。03

我採用兩百萬筆字串,因為我沒有足夠的主記憶體來容納五百萬個字串而不引起分頁置換(paging)。

為了知道時間花費在哪裡,我也執行了刻意遺漏 sort 的程式(見表格四)。我所準備的字串相對較短(平均由七個字元構成)。

表四 / 讀入並輸出字串 — 刻意遺漏 sort

C++ C C/C++

比值 C,去除

字串複製動作 最佳化後的

C/C++ 比值

500,000 筆資料 2。5 3。0 1。2 2 0。8

2,000,000 筆資料 9。8 12。6 1。29 8。9 0。91

注意,string 是一個很完美的使用者自定型別,而它只不過是標準程式庫的一部份而已。如果我們能夠因為使用 string 而獲得效率和精緻,我們也能夠因為使用其他使用者自定型別而獲得效率和精緻。

為什麼我要在程式設計風格和教學的文章中討論效率呢?因為,程式設計風格以及我們所教導的技術,必須為真實世界的問題服務。 C++ 的創造是為了運用於大規模系統以及對效率有嚴格規範的系統。因此我認為,如果 C++ 的某種教育方式會導致人們所使用的程式設計風格和技術只在玩具程式中才有效率可言,那是令人無法 同的,那會使人們挫敗並因而放棄學習。以上的量測結果顯示,如果你的 C++ 風格極為依賴泛型程式設計(generic programming)和具象型別,以此提供更簡單更達到「型別安全(type-safe)」的碼,其效率可以和傳統的 C 風格一較長短。類似的結果在物件導向(object-oriented)風格中也可獲得。

不同的標準程式庫實作品的效率表現,有戲劇性的差異,這是一個重要問題。對一個決定大量依賴標準程式庫(或廣為流傳的非標準程式庫)的程式員而言,很重要的一點是,你所採用的程式設計風格應該能夠在不同的系統上都有至少可被接受的效率。我很驚駭地發現,我的測試程式在某個系統上,C++ style 和 C style 相比有兩倍快,而在另一個系統上卻只有一半快。如果系統間的變動因素超過 4,程式員就不該接受。就我所能理解,這種變異性並非由於基本因素而形成,所以不需要程式庫實作者過份誇張的努力,就應該可以達到效率的一致性。採用最佳化程度較佳的程式庫,或許是改善對標準 C++ 的認知和實際效率表現的最輕易方式。是的,編譯器實作者很努力地消除各個編譯器之間的微小效率差異;我估量在效率方面,標準程式庫的實作者影響較大。

很明顯,上述 C++-style 解法相較於 C-style 解法所帶來的程式設計與邏輯上的簡化,可以藉由 C++ 標準程式庫而達到。這樣的比較是否不夠實在或不夠公平呢?我不這麼認為。C++ 的一個關鍵形貌就是,它對程式庫的支援能力,精緻而且高效。上述簡單程式所展現的種種優點,在任何應用領域中都可以保持 — 只要其間存在著精緻而高效率的程式庫。C++ 族群的挑戰在於擴充領域,讓一般程式設計師也都享受得到這些利益。也就是說,我們必須針對更多應用領域,設計並實作精緻而富有效率的程式庫,並讓這些程式庫被廣泛運用。

學習 C++

即使是專業程式設計師,也不可能一開始就先將整個語言的全貌學習完畢,然後才開始使用它。程式語言應該要分段學習,以小型的練習來試驗其種種設施。所以我們總是以分段精通的方式來學習一個語言。真正的問題不在於 “我應該先學習語言的一部份嗎?” 而在於 “我應該先學習語言的哪一部份?”

關於這個問題,傳統的回答是 “先學習 C++ 中與 C 相容的子集”。但是從我所思考的觀點來看,這不是一個好答案。這種學習法會導致過早專注於低階細節。它也會因為強迫學生過早面對許多技術難點而模糊了程式設計風格與設計上的議題,進而壓抑了許多有趣的東西。本文先前的兩個例子已經說明這一點。C++ 擁有較佳的程式庫支援,較佳的表示法,較佳的型別檢驗,無疑地在在對於 “C 優先” 的作法投了一張反對票。然而,注意,我也並不是說要 “純粹

Top