C++ Primer Plus 自學筆記1:C++特性及程序主體簡介

C語言特性

結構化編程(Structured Programming)

早期語言(如 Fortran 和 Basic )在代碼組織上可能會存在問題(例如分支語句常常會讓代碼邏輯和可讀性變得特別差)。對此,C語言使用了結構化編程這一方法,將程序中的分支限制爲一組易讀的代碼結構(例如 for、while、do while 循環以及 if else 語句)。

自頂向下( top-down )

將大型程序分解爲多個小型、易管理、可重複調用的小任務,這個分解過程可以持續下去,把小任務分解爲更小的任務。在C語言中,通過函數來實現“自頂向下”。

 

C++語言特性

面向過程編程

即 Procedure Oriented Programming(POP),以過程爲中心的編程思想。以“什麼正在發生”爲主要目標進行編程,是 C語言的重要特性(前文所述的結構化編程、自頂向下思想亦根植於此特性)。由於 C++ 繼承自 C,因此 C++ 也擁有此特性。

面向對象編程

即 Object Oriented Programming(OOP)。在 C++ 中通過類與對象這兩種數據結構及封裝、多態、繼承等方法實現。類規定了用何數據描述一個物像(或者概念)並規定了如何用這些數據去描述,對象則是根據類的要求構造的而成的,代表某個物像(或概念)的特殊數據結構。因此與面向過程不同,面向對象的重點不在任務上,而在於表示概念。

面向對象編程常常從低級組織(類)開始構建,進而構建高級組織(程序),該過程稱爲自下向上編程( bottom-up )

泛型編程

即Generic Programming。泛型也是一種特殊的抽象數據結構,相較於面向對象編程關注於數據,泛型編程關注算法本身,因此其不拘泥於數據類型。例如,寫一個兩數相加返回結果的函數,若輸入的數據類型可能爲int,也可能爲float,則比較麻煩,通過泛型(更具體地說,使用模板)則可較爲方便地解決:

#include <iostream>
using namespace std;

template <typename T>
T add(T a,T b){
    return a+b;
}

int main(){
    int n1, n2, n3;
    float f1, f2, f3; 
    cin>>n1>>n2;
    cin>>f1>>f2;
    n3 = add(n1, n2);
    f3 = add(f1, f2);
    return 0;
}

 

可移植性及C++標準

可移植性是指同樣的代碼,只需要使用針對不同平臺設計的編譯器進行編譯,而不必更改代碼本身,就可以跨平臺運行(這裏所說的平臺,是 CPU + 操作系統,也就是說,即使操作系統相同,CPU不同,也可能算作不同平臺)。然而程序的可移植性的實現有兩大阻礙。一是硬件,與硬件相關的程序不可移植。舉個例子,彙編語言的幾乎就是不可移植的,因爲它過於底層,直接操作硬件,然而不同的CPU有不同的指令集(不同的指令集意味着不同的彙編語法),因此同樣的彙編語言,可能換個CPU就不行了……二是標準。不同操作系統上,即使是同一種語言,由於標準不同語法上可能會有些許差異,這也是影響移植性的一大原因。

要解決第二種阻礙,可通過制定針對所有平臺的統一標準來實現。對此,ISO標準委員會分別於1998、2003、2011年推出了C++標準(分別爲C++98、C++03、C++11標準,其中C++03是C++98的補丁版,兩者語言特性一致)。之後也ISO也頒佈過C++14、C++17等標準……

同理,C語言也有國際標準,如C89、C90、C99等(其中C89和C90一樣)

雖然C++一般可以視爲功能更強大的C,但兩者並不能完全兼容。C++標準可“近似地”視爲C標準的超集,但也並不是所有的C語言的代碼都可正確運行於C++中。
C++ Primer Plus 書中所述基於C++98標準,並介紹部分C++11特性。

 

再識 A+B Problem

下面我們將逐行解釋以下程序

#include <iostream>
using namespace std;
int main(){
    int a, b, c;
    cin>>a>>b;
    c = a + b;
    cout<<c<<endl;
    return 0;
}

第一行:頭文件

頭文件的作用、編譯器對頭文件的處理方式(預編譯)這裏就不展開了,這裏講講頭文件的幾種形式:

  • C++的頭文件,用C++的寫法(不帶後綴名)。例如,iostream是C++的頭文件,調用時就寫爲:#include <iostream>
  • C++的頭文件,但用C的寫法(即使用.h)。例如:#include <iostream.h>
  • C的頭文件,用C的寫法,例如:#include <math.h>
  • C的頭文件,但後來被轉換爲C++的頭文件,則不需要後綴名,但要在前面加上“c”,例如:#include <cmath>

再講講語法規則:

如果include的是官方的標準頭文件,用尖括號“包圍”頭文件名,如:

#include <iostream>

預編譯時編譯器會在系統中存放標準頭文件的目錄中尋找。

如果是include的是用戶編寫的,非官方的頭文件,則用雙引號:

#include "abc.h"

這樣編譯器在預編譯的時候會優先在用戶目錄下搜索對應頭文件,沒有的話再到系統中存放標準頭文件的目錄中尋找。

第二行:命名空間(namespace)

假設程序引用的兩個頭文件a和b中,都有函數add的聲明,那在main中調用add就會產生衝突,因此可通過定義兩個命名空間A和B進行對應,那麼在函數調用時,A::add()調用的就是頭文件a中聲明的函數,B::add()調用的就是頭文件b中聲明的函數。

目前,C++標準的函數、類、變量等,都置於命名空間std之下,假設#include <iostream>後使用cin、cout,則需要寫出完整名字:std::cin和std::cout。當然,也可在main之前進行命名空間的聲明:

using std::cout;

之後在使用cout時,可不必再寫std::

當然,也可以直接聲明使用std命名空間中的所有名稱,從而在後續調用中免去所有std::

using namespace std;

該行命令可以寫在函數內,則該函數從這條命令後的調用可省略std::,也可以寫在函數外,則全局有效。

第三行:main函數

無論是單個cpp,還是大量文件的巨型工程,一般而言代碼中有且只有一個main函數(沒有main函數的其他cpp文件則被視爲庫文件,一般用於實現函數、類)。通常,我們視main函數爲操作系統與程序的橋樑。系統通過main,來開始執行我們的源代碼。

main函數無需參數傳入,因此也可以寫爲:int main(void)

main函數一般寫爲int類型,返回值爲0,然而實際上,根據C++標準,main函數可以省略掉return 0;當然也只有main函數有這個特殊待遇……

經典的C語言寫法往往還能省略掉main前面的int:

main()
{
    int a, b, c;
    c = a + b;
    cout<<c;
    return 0;    //這句也能再省略
}

此外還有寫成void main()的,大一的垃圾C++課用的VC 6.0支持的就是這個……不過這種寫法比較新的編譯器都不支持了 。

第四行:定義

沒什麼好說的,略了……

第五行:cin

按書中所述,cin和cout並不是函數,而是一種對象,cin爲istream類的對象,cout爲ostream類的對象,這兩個類均在iostream中定義。符號“<<”和“>>”是用於指示信息的流動方向,如cout<<c,意思就是變量c中的內容(信息)被插入到輸出流中。這裏還涉及到運算符重載的問題。符號“<<”和“>>”實際上是位運算中左移和右移的符號,但在輸入輸出流中它們被重載了。

第六行:賦值

也沒啥好說的,記一個不太熟的知識點:C++支持連續賦值。

int a = 1;
int d = c = b = a;

賦值依次進行,a的值賦給b,此時b==1,再把b的1賦值給c,依次類推。

第七行:cout

具體可看“第五行:cin”的相關內容,這裏記個知識點:控制符“endl”實際上通過iostream聲明,其全名爲std::endl。

另外,endl是控制符,換行符是“\n”。

第八行:返回值

前文提到了,略了……

第九行:空白

最後講講格式問題。

代碼中不可分割的元素叫做標記(token),例如int、main、return這些關鍵字、變量名等,標記之間一般需要用空白來隔開。所謂空白,是指空格、製表符(Tab)或者回車。

空白用於隔開標記,但實際上,並未規定用何種標記來隔開標記,因此程序可以寫成這個樣子:

#   include <iostream>
using 
namespace 
std
;
int             main
(       )
{return 0;}

也真是有夠噁心的……

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