當前位置:編程學習大全網 - 編程語言 - C和C++有什麽不同之處?

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

--------------------------------------------------------------------------------

將標準 C++ 視為壹個新語言

Learning Standard C++ as a New Language

作者 Bjarne Stroustrup

譯者 陳崴

就別再把 C++ 視為 C 的後壹個語言了吧。這個問題問 C++ 之父就對了。

侯捷註:本文系北京《程序員》雜誌 2001/04 的文章。譯筆順暢,技術飽滿。

承譯者陳崴先生與《程序員》雜誌負責人蔣濤先生答允,

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

未得陳崴先生與蔣濤先生二人之同意,任何人請勿將此文再做轉載。

--------------------------------------------------------------------------------

C/C++ User's Journal May,1999

Learning Standard C++ as a New Language

by Bjarne Stroustrup

--------------------------------------------------------------------------------

導入

想要獲得標準 C++ [三考資料 1] 的最大優點,我們必須重新思考 C++ 程式的撰寫方式。重新思考的方式之壹就是,想想 C++ 應該如何學習(和教育)。我們應該強調什麼樣的編程技術?我們應該先學習這個語言的哪壹部份?在真正程式碼中我們想要突顯的又是哪壹部份?

這篇文章把幾個簡單的 C++ 程式拿來比較,其中有些以現代化風格(使用標準程式庫)撰寫,有些以傳統的 C 語言風格撰寫。從這些簡單例子所學到的功課,對大程式而言仍然具有重要意義。大體而言,這篇文章主張將 C++ 視為壹個更高階的語言來使用,這個語言依賴抽象性提供簡練與優雅,但又不失低階風格下的效率。

我們都希望程式容易撰寫,正確執行,易於維護,並且效率可被接受。這意味我們應該以最接近此壹理想的方式來使用 C++(或任何其他語言)。我猜想 C++ 族群尚未能夠消化標準 C++ 所帶來的種種設施;重新思考我們對 C++ 的使用方式,可獲得壹些重要的改善並進而達到上述理想。本文所重視的編程風格,焦點在於充份運用標準 C++ 所支援的設施,而不在那些設施本身。

主要的改善關鍵就是,透過對程式庫的運用,降低我們所寫的碼的大小和復雜度。以下我要在壹些簡單例子中,示範並量化這些降低程度。這類簡單實例可能出現在任何 C++ 導入性課程中。

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

復雜度 Complexity

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

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

讀入名字

輸出“Hello <name>”

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

#include<iostream> // 取得標準 I/O 設施

#include<string> // 取得標準 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<stdio. h> // 取得標準的 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<stdio. h> // 取得標準的 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<stdio.h>

#include<ctype.h>

#include<stdlib.h>

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<stdlib.h>

#include<stdio.h>

// 壹個比較函式,稍後將給 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 <vector>

#include <fstream>

#include <algorithm>

using namespace std;

main(int argc, char*argv[])

{

char* file = argv[2];

vector<double> 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 版本。我忘記含入<algorithm>;我留下了 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<vector>

#include<fstream>

#include<algorithm>

#include<string>

using namespace std;

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

{

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

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

vector<string> 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<string> (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 優先" 的作法投了壹張反對票。然而,註意,我也並不是說要 "純粹

  • 上一篇:CPU、GPU、APU有那些型號?
  • 下一篇:神經科學和vr將碰撞出怎樣的新世界
  • copyright 2024編程學習大全網