位元詩人 [C++] 程式設計教學:基本概念

C++
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

前言

本文假定讀者沒寫過 C++ 程式或僅寫過少量 C++ 程式,介紹 C++ 的基本概念,讓讀者可以開始寫第一個 C++ 程式。

C++ 程式碼所用的副檔名 (Filename Extension)

C++ 程式碼承襲 C 的慣例,分為原始碼 (source) 及標頭檔 (header) 兩部分。前者是程式的內部實作,後者是程式的外部界面。

原始碼所用的副檔名有:

  • .cc
  • .cpp
  • .cxx
  • .c++ (不建議)
  • .C (大寫 C)

標頭檔所用的副檔名如下:

  • .hh
  • .hpp
  • .hxx
  • .h++ (不建議)
  • .H (大寫 H)

由於 .c++.h++ 有用到特殊符號 +,不建議使用。其他的副檔名皆可使用,只要在專案內保持一致即可。

C++ 程式碼所用的檔案名稱

C++ 沒有規範檔案名稱,檔名也不需要和檔案內的類別 (class) 名稱保持一致 (註) 。基本上,只要簡單易懂即可。以下是一些簡單的原則:

(註) 大部分程式語言都沒這個限制。僅有 Java 等少數語言有這項「特性」

  • 不要在目錄或檔案名稱中使用空白
  • 使用英文字母、阿拉伯數字、底線的組合來命名目錄和檔案
  • 使用簡短的單字或常用的縮寫來命名目錄和檔案
  • 儘量不要自創沒人用過的縮寫
  • 可在 PascalCasecamelCasesnake_casekebab-case (註) 中擇一
  • 在整個專案內保持一致的命名方式

(註) C++ 識別字不能用 kebab case 來命名,但檔名沒這項限制

雖然 C++ 編輯器沒有強制規定,原始碼和標頭檔可用相同的檔案名稱,更容易識別。例如 point.cpppoint.hpp

撰寫第一個 C++ 程式

以下是最簡單的 C++ 程式碼:

int main() {}

這個程式什麼事都沒做,也看不出 C++ 程式的架構。我們另寫一個簡單的 Hello World 程式。用編輯器建立空白的 hello.cpp 文字檔案,加入以下內容:

/* Include standard input/output library. */
#include <iostream>

/* The main function. */
int main(void)
{
    /* Print "Hello World" to standard output. */
    std::cout << "Hello World" << std::endl;

    /* Return the program exit state. */
    return 0;
}

雖然這個程式很簡單,但可觀察 C++ 程式的基本架構。

編譯 C++ 程式的步驟

編譯 C++ 程式碼的步驟可拆分如下:

  • 前處理 (preprocessing)
  • 編譯 (compilation)
  • 組譯 (assembly)
  • 連結 (linking)

前處理是利用前置處理器的巨集語言進行字串代換的過程。嚴格來說,前置處理器不是 C++ 的一部分,而是和 C++ 共生的小型語言。由於 C++ 支援真正的泛型程式,不需要使用前置處理器模擬泛型程式。

這裡的編譯是狹義上的編譯,也就是把 C++ 程式碼轉換為等效的組合語言程式碼。由於 C++ 語法無法直接一對一轉換成組語,可知 C++ 是高階語言。

組譯將組語程式碼轉為目的檔 (object file),再經由連結轉為可運行的執行檔 (executable),就算是完成編譯 C++ 程式碼的過程。

C++ 專案 (Project)

軟體專案將程式碼、編譯設定檔、說明文件等內容組織在一起,便於程式設計者管理該軟體的程式碼。C++ 本身沒有專案的概念,在 Visual Studio 或 Xcode 中見到的 C++ 專案是開發工具所加上的功能,並非 C++ 的一部分。

實務上,會用 Make、CMake 等跨平台的專案管理工具來管理 C++ 專案,讓 C++ 專案和 IDE 脫勾。除非該專案的 C++ 程式碼是平台限定的,不應該用特定 IDE 綁住 C++ 專案。

大小寫敏感性 (Case Sensitivity)

C++ 編譯器會區分大小寫,所以 fooFooFOO 視為相異的識別字 (identifier)。但不建議濫用這項特性,最好還是以不同名稱命名不同識別字。

空白 (Space)、縮進 (Identation)、換行 (Newline)

C++ 程式碼會以空白來區分識別字、保留字、實字、運算子等,但不限制空白的數量。同理,C++ 也不規範縮進。使用空白和縮進的目的是將 C++ 程式碼排列整齊,易於閱讀和維護。

由於 C++ 使用分號 ; 做為敘述結束的符號,C++ 不嚴格規範換行。適當地換行是為了讓程式碼更加美觀易讀。

表達式 (Expression) 和敘述 (Statement)

表達式和敘述是程式設計的基本概念。表達式會回傳值,敘述會完成某項任務。C++ 承襲 C 的慣例,以敘述做為程式碼的單位。

註解 (Comment)

C++ 有兩種風格的註解:

  • 多行註解:以一對 /**/ 包住。來自 C
  • 單行註解:以 // 開頭的單行文字

以下是單行註解:

// A single line comment.

以下是多行註解:

/* A multiline comment that
    crosses several lines.  */

C++ 編譯器會自動去除註解的部分,故註解內可寫入自由文字 (free text),不需要寫 C++ 程式碼。

撰碼風格 (Coding Style)

在程式碼中使用一致的撰碼風格,會增加程式碼的可讀性。實務上,會以開發團隊所用的撰碼風格為準。對於獨立開發者來說,則在自身的程式碼中保持一致即可。

C++ 常見的撰碼風格有 K&R、BSD、GNU 等。台灣的 C++ 教材多使用 K&R 風格。本系列文章的程式碼會以 K&R 風格為主。

標準輸出入 (Standard Input and Standard Output)

輸出入是電腦程式和外界溝通、交換資料的途徑。剛開始學習 C++ 時,撰寫的 C++ 程式為終端機程式。這類程式會以標準輸入 (standard input)、標準輸出 (standard output)、標準錯誤 (standard error) 等途徑和命令列環境溝通。

C++ 使用以下物件來處理標準輸出入:

  • std::cin:標準輸入 (standard input)
  • std::cout:標準輸出 (standard output)
  • std::cerr:標準錯誤 (standard error)

以下 C++ 敘述將 "Hello World" 字串輸出到標準輸出,並附加 (跨平台的) 換行符號:

std::cout << "Hello World" << std::endl;

主函式 (Main Function)

主函式是為 C++ 程式的進入點。每個 C++ 應用程式只能有一個主函式,該函式的名稱固定為 main

若不需要命令列參數時,主函式的寫法如下:

int main(void)
{
    /* Implement code here. */
}

反之,若需要命令列參數時,則改用以下寫法:

int main(int argc, char **argv)
{
    /* Implement code here. */
}

命令列參數的數量存在 argc 參數中,該參數的型態是整數。而命令列參數的字串存在 argv 參數中,該參數的型態是 C 字串陣列。C++ 的命令列參數刻意相容於 C,沿用 C 字串陣列而非使用 C++ 的字串型態。

標準函式庫 (Standard Library)

C++ 標準函式庫是一組預寫好的宣告、函式、類別等。由於標準函式庫已經實作好常見的功能,C++ 程式設計者不需重覆實作。除非有很好的理由,能用標準函式庫的功能就不應自行重造輪子。

引入函式庫

使用前置處理器的 #include 敘述可以引入 C++ 函式庫。引入函式庫有兩種寫法。

引入標準函式庫和第三方函式庫時會使用一對 <>

#include <iostream>

引入內部函式庫時會使用一對雙引號 "

#include "mylib.hpp"

使用第二種寫法時,會優先從專案內部找尋 C++ 標頭檔。

在 C++ 中使用 C 標準函式庫

C++ 可以使用 C 標準函式庫,但被引入的 C 標頭檔要經適當的處理。這裡暫時不談撰寫給 C++ 用的 C 標頭檔的方式。

在 C++ 中要使用 C 標準函式庫時,要加上前綴 c 並略去副檔名 .h。例如,以下 C++ 敘述引入 C 的標準輸出入函式庫 stdio.h

#include <cstdio>

引入的 C 函式、巨集等宣告的名稱保持原狀,不需修改即可直接使用。

雖然 C++ 不是 C 的嚴格超集合,但 C++ 刻意在特性上相容 C,所以保留在 C++ 中使用 C 函式庫的能力。

命名空間 (Namespace)

C 沒有命名空間,所以要用前綴或後綴避免函式名稱衝突。C++ 用命名空間代替函式前綴,解決函式名稱衝突的議題。像 std::coutstd 就是命名空間。

有些網路上的 C++ 程式碼或少數 C++ 教材藉由以下方式來使用命名空間:

#include <iostream>

/* DON'T DO THIS IN PRODUCTION CODE. */
using namespace std;

int main(void)
{
    /* Implement code here. */
}

using namespace std; 敘述的意思是使用 std 做為此程式的命名空間。由於 std 剛好是標準函式庫所用的命名空間,這行敘述相當於不加前綴,直接在程式中使用標準函式庫內的函式或物件,像是 coutcerrendl 等。

這樣的寫法,會在自身程式的命名空間中一次帶入過多函式或物件,有時候不小心用到同名的識別字,造成程式錯誤。比較好的方式,是只帶入所需的函式或物件:

#include <iostream>

using std::cout;
using std::endl;

int main(void)
{
    cout << "Hello World" << endl;

    return 0;
}

離開狀態 (Exit Status)

在 C++ 程式的末端,可以看到以下敘述:

return 0;

該敘述會回傳整數 0 給系統。這個數字即為離開狀態碼。按照程式設計的慣例,0 固定為成功地離開程式,非零值 1 為程式出現錯誤。

回傳值的重點在於零和非零。有少數程式會設計複雜的離開狀態值。但不同程式間的離開狀態值並沒有一致的共識,該程式的使用者得查閱此程式的使用手冊才能得知特定回傳值的意義。

關於作者

身為資訊領域碩士,位元詩人 (ByteBard) 認為開發應用程式的目的是為社會帶來價值。如果在這個過程中該軟體能成為永續經營的項目,那就是開發者和使用者雙贏的局面。

位元詩人喜歡用開源技術來解決各式各樣的問題,但必要時對專有技術也不排斥。閒暇之餘,位元詩人將所學寫成文章,放在這個網站上和大家分享。