Stanley Lippman:c++ Primer (中文版 第3版 潘爱民译)

C++ Primer 第三版 核心觀念闡述

本文件旨在提取並詳盡解釋《C++ Primer 第三版》一書(包含譯序及前言至第二十章)中的主要論點與觀念。

譯序與前言:理解 C++ 的演進與本書的特色

譯序和前言部分共同為讀者建立了理解 C++ 語言及其本書價值觀念的基礎。核心論點包括:

  1. C++ 的成熟與標準化: C++ 是一門有近 20 年歷史的傳統語言,但也因 1998 年完成 ISO 標準化而成為一門「新」語言。標準化帶來了語言特性的統一、標準函式庫的建立以及更好的程式碼可移植性。本書正是基於這個新標準來闡述 C++,強調不應再將 C++ 簡單地視為 C 語言的超集。
  2. 標準函式庫的重要性: 標準 C++ 引入了覆蓋面極廣的標準函式庫,特別是以前稱為 STL(Standard Template Library)的部分,包含了容器(如 vector, list, map, set)和泛型演算法。這大大豐富了 C++ 的基礎設施。
  3. C++ 對多重程式設計範式的支援: C++ 不僅支援物件導向(Object-Oriented)程式設計,也支援程序式(Procedural)和基於物件(Object-Based,即抽象資料類型)的程式設計。這種多範式支援使得 C++ 能夠為不同的問題提供最適合的解決方案,但也增加了語言的複雜性。
  4. 本書的權威性與深度: 本書由 C++ 領域的專家和標準委員會成員撰寫,結合了實作經驗與標準規範,具有權威性。儘管名為 “Primer”,本書內容深入,涵蓋了許多 C++ 的複雜特性,如函式重載解析、模板機制、異常處理等。因此,本書並非輕量的入門讀物,需要讀者投入時間反覆閱讀和練習。
  5. 本書的結構與特色: 本書結構清晰,圍繞一系列可擴展的範例來介紹語言特性,這有助於讀者理解特性背後的動機和實際用法。特色包括:內容廣泛、提供大量實用範例、敘述安排體現多範式特點、與編譯器無關且遵從標準、提供配套練習。

第一篇:C++ 概述 (Chapters 1-2)

這部分提供了對 C++ 全貌的快速瀏覽,介紹了問題解決的基本策略以及 C++ 語言的一些核心概念和機制。

第一章 開始:程式的基礎構成

本章從解決問題的方法(分而治之、逐步求精)入手,介紹了 C++ 程式的基礎構成元素:

  1. 程式的基本單位: 程式由陳述句 (statement) 組成,最簡單的是空陳述句。表達式 (expression) 是動作,以分號結尾的表達式可構成陳述句(如宣告、賦值、輸出)。
  2. 函式與 main(): 函式 (function) 是將陳述句按邏輯語義分組的命名單元。每個 C++ 程式必須包含 main() 函式,它是程式執行時的第一個函式。函式由返回類型、函式名、參數表和函式體組成,可使用 return 陳述句返回值。
  3. 編譯與執行: C++ 程式的建立涉及原始程式碼檔案(通常有 .c, .C, .cxx, .cpp 等後綴)的編寫、預處理 (preprocessing)、編譯 (compilation)、連結 (linking) 生成可執行檔。編譯器會檢查語法錯誤和類型錯誤。
  4. 基本資料類型: 介紹了 C++ 內建的算術類型 (char, int, short, long, float, double, long double) 和布林類型 (bool),以及它們的有符號/無符號、長度差異。
  5. 標準函式庫基礎: 初步引入標準函式庫提供的抽象類型,如 stringvector,並指出這些類型也是基於 C++ 的類機制實現的。
  6. 流程控制: 介紹了 if 陳述句用於條件執行,以及 whilefor 迴圈用於重複執行。
  7. 預處理器指令: 介紹 #include 用於包含頭文件, #ifndef/#define/#endif 用於防止頭文件重複包含,#ifdef 用於條件包含程式碼。提及預定義的預處理器宏和 C 標準庫的 assert() 宏。
  8. 註解: 介紹 /* */// 兩種註解形式及其用法,強調註解的有效性。
  9. 基本輸入/輸出: 介紹 iostream 庫,預定義的 cin, cout, cerr 物件,以及 << (輸出) 和 >> (輸入) 操作符,endl 操縱符,並初步展示文件輸入輸出 (fstream)。

第二章 C++瀏覽:多範式與核心機制預覽

本章通過一個具體的範例(數組抽象的演進)快速預覽了 C++ 對不同程式設計範式的支援以及幾個核心機制:

  1. 內建數組的限制: 介紹內建數組作為同類型元素的集合,使用下標 [] 訪問元素。指出其限制,如不支援整體操作(賦值、比較)、缺乏自我意識(大小未知)以及作為函式參數時的限制。
  2. 動態記憶體與指標: 簡要介紹動態記憶體分配 (dynamic memory allocation) 的概念(使用 newdelete),以及指標 (pointer) 作為存放記憶體地址的類型,用於間接操作對象,特別是動態分配的對象。
  3. 基於物件的設計 (類): 引入類 (class) 機制作為支援抽象資料類型的方式。介紹類定義 (class head, class body, public/private 訪問控制)、成員函式 (member function / method) 和資料成員 (data member)。通過 IntArray 類展示如何將操作(賦值、比較、查找、排序等)和數據封裝在一起,實現資訊隱藏 (information hiding),並利用內聯函式 (inline function) 提高效能。介紹構造函式 (constructor) 作為自動初始化的機制和函式重載 (function overloading)。
  4. 物件導向的設計 (繼承與虛擬函式): 在基於物件設計的基礎上,引入繼承 (inheritance) 和動態綁定 (dynamic binding) 機制,支援物件導向。通過 IntArray 派生 IntArrayRC (帶範圍檢查) 展示繼承如何實現程式碼重用。介紹基類 (base class) 和派生類 (derived class / subtype) 的關係 (is-a),保護 (protected) 訪問級別。引入虛擬函式 (virtual function) 實現動態綁定,使得通過基類指標/引用呼叫方法時,實際執行的函式根據對象的實際類型在執行時決定。
  5. 泛型設計 (模板): 引入模板 (template) 機制,允許將類型或值參數化。通過將 IntArray 泛化為 Array 模板,展示如何自動生成處理不同類型元素的類實例。介紹函式模板 (swap) 作為自動生成處理不同類型參數的函式的方式。
  6. 異常處理: 初步介紹異常 (exception) 作為運行時不正常情況的表示,以及 try, throw, catch 關鍵字構成的異常處理機制。
  7. 名字空間: 介紹名字空間 (namespace) 機制,用來封裝名字,避免全域名字衝突,特別是標準函式庫中的名字被放在 std 名字空間中。介紹合格名字 (qualified name) 和 using 指示符/宣告。
  8. 標準函式庫容器與演算法: 再次提及 vector 作為標準庫提供的數組抽象(相對於內建數組的優勢),並首次介紹泛型演算法 (generic algorithm) 可以操作容器元素,如 sort, find。提及 map 關聯容器。

第二篇:基本語言 (Chapters 3-6)

這部分深入探討了 C++ 程式的基本構成要素:資料類型、表達式和陳述句。

第三章 C++資料類型:程式碼的基石

本章詳細介紹了 C++ 支援的各種資料類型:

  1. 資料類型總覽: 強調資料類型決定了記憶體大小、值範圍及可應用操作。介紹內建數值類型、字元類型、布林類型,以及標準函式庫的 stringcomplexvectorpair 等抽象類型。
  2. 文字常量: 詳細介紹各種類型文字常量的寫法、類型後綴、轉義序列,並指出字符串文字是常量字元數組。
  3. 變數: 定義變數為具名記憶體儲存區,具備類型、值和地址(左值/右值)。區分宣告 (declaration) 與定義 (definition)。討論變數命名規則、初始化方式(顯式賦值、括號初始化、預設初始化、成員初始化列表),並指出未初始化對象的危險性。
  4. 指標類型: 詳細介紹指標作為存放地址的類型,指標的類型決定了對記憶體內容的解釋方式。void* 作為通用指標。解引用操作符 * 訪問指標指向的對象。指標算術運算及其在數組遍歷中的應用。
  5. 字串類型: 再次討論 C++ 的兩種字串表示:C 風格字串(char* 和 C 庫函式)和標準庫 string 類型(作為物件導向抽象的優勢、常用操作:大小、空判斷、賦值、比較、連接、查找、子字串、替換、c_str() 轉換)。
  6. const 限定修飾符: 介紹 const 使對象成為常量(只讀),必須初始化。const 對指標和引用的影響(指向 const 的指標 vs. const 指針),在函式參數中的應用。
  7. 引用類型: 介紹引用作為對象的別名,必須初始化且不能重新綁定。引用作為函式參數(傳遞大型對象、返回多個值)的常用場景。區別引用與指標。
  8. 布林類型: truefalse 文字常量。與整數類型的自動轉換。
  9. 枚舉類型: enum 定義具名整數常量的集合,每個枚舉定義一個獨立類型,限制值的有效範圍。
  10. 數組類型: 再次詳細介紹內建數組的定義、初始化、索引(從 0 開始)、多維數組。指出其不支持整體操作和缺乏自我意識的限制。數組名可被解釋為指向首元素的指標,引入數組與指標的關係。
  11. vector 容器類型: 再次介紹 vector 作為內建數組的替代,提供更豐富的操作(大小、空判斷、插入、刪除、與其他 vector 的初始化/賦值),支持動態增長。介紹數組習慣和 STL 習慣兩種用法。
  12. complex 類型: 標準庫複數類型,支持算術操作和 I/O。
  13. typedef 名稱: 為已有類型提供別名,提高可讀性,簡化複雜宣告。
  14. volatile 限定修飾符: 指出對象的值可能在編譯器控制外改變,影響編譯器優化。
  15. pair 類型: 標準庫類型,在單一對象中組合兩個值。
  16. 類類型: 再次引入類作為定義新類型的方式,通過 String 類初步展示類成員、操作符重載、構造函式、析構函式等概念。

第四章 表達式:如何運算與控制流程細節

本章深入探討 C++ 的表達式以及如何組合操作符和控制運算流程:

  1. 表達式構成: 表達式由操作數 (operand) 和操作符 (operator) 組成,單元或複合。
  2. 算術操作符: +, -, *, /, %。整數除法的截斷,求餘 (%) 的限制與符號問題。算術異常(溢出、下溢)可能導致未定義行為。
  3. 等於、關係、邏輯操作符: ==, !=, <, >, <=, >= 返回布林值。!, &&, || 邏輯操作符。&&|| 的短路求值特性及其應用。關係操作符的潛在陷阱(運算順序未定義、鏈式比較)。
  4. 賦值操作符: = 將右值賦給左值。+=, -=, 等複合賦值操作符。賦值操作符的左操作數必須是左值。賦值表達式本身有返回值。
  5. 遞增與遞減操作符: ++, -- 用於加一或減一。前置 (++var) 和後置 (var++) 形式的區別及其在棧實現中的應用示例。
  6. 複數操作: 標準庫 complex 類型支持內建算術操作符、複合賦值操作符、I/O 操作符等,可與內建類型混合運算。
  7. 條件操作符: expr1 ? expr2 : expr3 作為簡單 if-else 的替代。
  8. sizeof 操作符: 返回對象或類型名的位元組數。結果是 size_t 類型。用於數組時返回整個數組大小。在編譯時計算,是常量表達式。
  9. newdelete 表達式: 動態記憶體分配與釋放。new type(value) 分配並初始化單個對象。new type[size] 分配數組(內建類型不初始化)。delete pointer 釋放單個對象。delete [] pointer 釋放數組。動態分配對象是無名的。忘記釋放導致記憶體洩漏。誤用 delete 的危險。
  10. 逗號操作符: , 串聯表達式,從左到右求值,結果是最右邊表達式的值。
  11. 位操作符: ~, <<, >>, &, ^, |。操作數被解釋為位集合。與布林邏輯操作符的區別。
  12. bitset 操作: 標準庫 bitset 類提供位集合抽象,相比直接操作整數位更安全、易用。提供設置、測試、翻轉、計數、大小等操作。
  13. 操作符優先級: 決定複合表達式中操作的求值順序。括號可改變優先級。關聯性決定相同優先級的操作符求值順序。
  14. 類型轉換: 發生在混合類型表達式、賦值、函式參數/返回值等場景。
    • 隱式類型轉換:編譯器自動進行的標準轉換。
    • 算術轉換:提升操作數至共同類型以防止精度損失。
    • 顯式類型轉換:使用 static_cast, const_cast, dynamic_cast, reinterpret_cast 強制轉換,可能繞過類型檢查,存在危險。
    • 舊式強制類型轉換:C 語言風格的 (type)expr 或 C++ 風格的 type(expr),功能更廣泛但也更危險、可讀性差。

第五章 陳述句:程式的流程控制

本章詳細介紹了 C++ 中控制程式執行流程的陳述句:

  1. 簡單與複合陳述句: 單個陳述句以分號結束。複合陳述句 {} 包圍多個陳述句,形成一個區塊 (block),可替代單個陳述句。空陳述句 ; 和空複合陳述句 {}
  2. 宣告陳述句: 變數定義 (int ival;) 可出現在任何允許陳述句的地方,實現宣告的局部性 (locality of declaration)。在控制結構(if, switch, for, while)條件中宣告變數。
  3. if 陳述句: 條件執行。if (condition) statementif (condition) statement1 else statement2condition 可為表達式或宣告。空懸 else 問題及其解決方法。 if-else 鏈。
  4. switch 陳述句: 從一組互斥常量值中選擇執行路徑。case 標籤必須是常量表達式。break 陳述句跳出 switch。省略 break 導致穿透 (fallthrough)。default 標籤處理所有不匹配的情況。陷阱:忘記 breakcase 值非法或重複、在 case 中宣告未用 {} 包圍。
  5. for 迴圈陳述句: 常見用於遍歷固定長度數據結構。包含初始化、條件、表達式三部分。可省略部分或全部。初始化部分可宣告變數,其作用域通常僅限於 for 迴圈及其嵌套區塊。
  6. while 迴圈陳述句: 當條件為真時重複執行。條件可為表達式或宣告。
  7. do-while 迴圈陳述句: 至少執行一次,然後再測試條件。條件只能是表達式。
  8. break 陳述句: 跳出最內層的 while, do-while, forswitch 陳述句。
  9. continue 陳述句: 跳過當前迴圈迭代的剩餘部分,開始下一次迭代。僅用於迴圈陳述句。
  10. goto 陳述句: 無條件跳轉到同一函式內的標號 (label)。不鼓勵使用,使程式碼流程難以理解和維護。不能跳過未用 {} 包圍的宣告。
  11. 鏈表範例: 通過實現單向鏈表類 (ilistilist_item) 練習陳述句的用法,並逐步引入成員函式、構造/析構函式、指標、引用、操作符重載等概念。

第六章 抽象容器類型:處理集合數據

本章聚焦於標準函式庫提供的容器類型,特別是序列容器和關聯容器:

  1. 容器概述: 容器是處理集合數據的抽象。序列容器(vector, list, deque)和關聯容器(map, set, multimap, multiset)。
  2. vector vs. list 比較兩種序列容器的優劣勢(隨機訪問、插入/刪除效率、記憶體連續性),提供選擇容器的準則。
  3. vector 的動態增長: vector 通過預留 (reserve) 容量和重新分配 (reallocation) 實現高效增長,而非每次插入都重新分配。討論容量 (capacity) 與長度 (size) 的區別,不同類型元素對增長效能的影響。reserve() 成員函式。
  4. 定義序列容器: 使用 <vector>, <list>, <deque> 頭文件。介紹各種初始化方式(空容器、指定長度、指定長度並賦初值、用迭代器對或指標對初始化、用另一個容器初始化)。resize() 改變長度。容器之間關係操作符(比較)。容器元素類型必須滿足的要求(相等、小於、預設值)。
  5. 迭代器: 迭代器 (iterator) 提供通用遍歷容器的方式。begin() 指向首元素,end() 指向尾後位置。++ 前進,* 解引用。const_iterator 用於遍歷常量容器。迭代器算術僅適用於隨機訪問迭代器。迭代器對可標記範圍,用於初始化或傳遞給演算法。istream_iteratorostream_iterator 用於流。
  6. 序列容器操作: push_back(), push_front(), insert() (在指定位置插入單個/多個/範圍元素)。erase() (刪除單個/範圍元素), pop_back(), pop_front() (刪除首尾元素)。賦值 (=) 和交換 (swap()) 操作。
  7. 泛型演算法: 再次強調泛型演算法 (generic algorithm) 獨立於容器類型,通過迭代器對操作元素。許多容器操作(如 find, sort)是作為泛型演算法提供的。
  8. 文字查詢系統範例 (部分): 通過實現文字查詢系統逐步展示容器用法。
    • 儲存文本行:使用 vector<string> 儲存文件內容,getline() 讀取每行。
    • 查找子字串:使用 string 的查找操作 (find, find_first_of, find_last_of, rfind, find_first_not_of, find_last_not_of) 將行分解為單詞。substr() 提取子字串。
    • 處理標點符號:使用 find_first_of()erase() 移除單詞中的標點。
    • 處理其他格式:使用 tolower() 處理大小寫,初步處理單詞後綴。
    • 其他 string 操作:erase() (用迭代器或位置/數量刪除),insert() (在指定位置插入字串/字元),assign()append() (賦值和附加),swap(), at() (帶範圍檢查的字元訪問), compare() (字典序比較), replace() (替換子字串)。
  9. map 關聯容器: 介紹 map 作為鍵/值對集合,通過鍵索引值,元素有序。用於建立單詞位置映射表(單詞作為鍵,位置向量作為值)。定義 map (map<Key, Value>), 插入元素(下標操作符 vs. insert()), 查找 (count(), find()),遍歷 map (迭代器指向 pair 物件),刪除 (erase())。
  10. 建立單詞排除集 (set): 介紹 set 作為具備唯一鍵值的集合,用於判斷值是否存在,元素有序。用於建立排除無意義單詞的集合。定義 set (set<Type>), 插入元素 (insert()), 查找 (count(), find()), 遍歷 setinserter 迭代器适配器用於插入元素。
  11. multimapmultiset 介紹 multimapmultiset 允許鍵重複,與 mapset 相似。訪問重複鍵的元素需要迭代器 (find() 結合 count()equal_range())。不支持下標操作符。
  12. stack 標準庫 stack 作為容器適配器,預設由 deque 實現,支援後進先出 (push, pop, top, size, empty)。
  13. queuepriority_queue queue 支援先進先出 (push, pop, front, back, size, empty)。priority_queue 支援按優先級獲取元素。

第三篇:基於過程的程式設計 (Chapters 7-12)

這部分詳細探討了 C++ 中如何將基本語言元素組織成函式,以及相關的機制和特性。

第七章 函式:程式碼的組織單元

本章詳細介紹了函式的定義、宣告和使用:

  1. 函式概述: 函式是程式碼的組織單元,包含一組實現特定任務的陳述句。函式由返回類型、名稱、參數列表和主體組成。main() 是程式入口。函式呼叫 (invocation) 涉及參數傳遞。函式需要先宣告 (declaration) 後使用。函式宣告(原型 function prototype)描述介面。
  2. 函式原型: 返回類型(可為大多數類型,不可為數組或函式類型)、參數列表(逗號分隔的參數類型,可選參數名,空參數列表可用 void 表示)。參數類型檢查 (strong type-checked)。
  3. 參數傳遞: 參數傳遞方式決定了函式如何接收實參 (argument)。
    • 按值傳遞 (pass-by-value):預設方式,傳遞實參值的拷貝。函式內修改不影響實參。
    • 指標參數:傳遞實參地址,函式可間接修改實參。
    • 引用參數 (reference parameter / pass-by-reference):傳遞實參的左值,函式可直接修改實參。常用於傳遞大型對象或返回多個值。const 引用參數用於傳遞大型對象同時保證不修改。指標引用。
    • 數組參數:數組永遠不按值傳遞,實質是傳遞指向首元素的指標。函式內修改影響原始數組。函式不知數組長度,常需額外參數。數組的引用參數包含數組長度資訊。
    • 抽象容器類型參數:容器可按值或按引用傳遞。按引用傳遞效率更高。
    • 預設實參 (default argument):為參數指定預設值,呼叫時可省略相應實參。預設實參必須從右邊開始指定。
    • 省略號 (ellipsis):... 暫停類型檢查,允許傳遞未知數目和類型的實參。
  4. 返回值: return 陳述句結束函式並可返回值。返回 void 表示無返回值。返回值按值傳遞(預設)。可返回指標或引用。返回局部對象的引用是錯誤的。返回值也可以用作左值。函式參數和返回值 vs. 全域對象:建議通過參數傳遞而非全域對象進行函式間通信。
  5. 遞歸: 函式直接或間接呼叫自身。必須有停止條件。
  6. 內聯函式: inline 關鍵字建議編譯器將函式體在呼叫點展開,消除函式呼叫開銷,用於優化小型頻繁呼叫函式。只是建議,非保證。內聯函式定義通常放在頭文件中。
  7. 連結指示符: extern "C" 等用於指定與其他語言編寫的函式(如 C 函式)的連結規則。可為單一陳述句或複合陳述句。
  8. main() 程式的入口。可接收命令行參數 int argc, char *argv[]
  9. 指向函式的指標: 指標可指向函式。宣告語法(返回類型 (*指標名)(參數列表))。初始化與賦值。通過指標呼叫函式。函式指標的數組。用作函式參數或返回值。指向 extern "C" 函式的指標。

第八章 域和生命期:名字的有效範圍與對象的存活時間

本章探討名字在程式碼中的可見範圍(域)以及對象在運行時的存活時間(生命期):

  1. 域: C++ 中的每個名字都指向唯一的實體,域是名字的語義上下文。包括全域域 (global scope / global namespace scope)、局部域 (local scope / function block)、類域 (class scope)、名字空間域 (namespace scope)。名字解析 (name resolution) 是將表達式中的名字與宣告關聯的過程。
  2. 局部域: 函式定義或複合陳述句內的區域。可嵌套。名字解析從內向外查找。在控制結構(for, if, switch, while)條件中宣告的變數作用域。
  3. 全域對象和函式: 在全域域宣告的對象 (global object) 和函式 (global function)。程式整個執行過程中存在。全域對象/非內聯函式在程式中只能定義一次 (ODR, One Definition Rule)。宣告 vs. 定義 (extern 關鍵字)。不同文件間宣告的匹配。類型安全連結 (type-safe linkage)。頭文件的作用(局部性、一致性)。
  4. 局部對象: 在局部域宣告的對象。包括自動對象 (automatic object)、寄存器對象 (register object)、局部靜態對象 (local static object)。區別在於存儲區屬性與生命期。自動對象在函式呼叫時分配,結束時釋放(棧上)。寄存器對象是建議放在寄存器中的自動對象。局部靜態對象程式執行期間一直存在,首次執行到宣告點時初始化。未初始化自動對象值未指定,局部靜態對象初始化為 0。返回局部對象的地址是錯誤的(空懸指標)。
  5. 動態分配的對象: 使用 newdelete 在空閒儲存區 (free store / heap) 分配和釋放的對象,生命期由程式員控制。可分配單個對象或數組。數組長度可在運行時確定。
    • 單個對象分配:new type(value) 分配並可初始化。返回指標。
    • 釋放:delete pointer。誤用 delete 的危險(記憶體洩漏、重複刪除、使用已釋放記憶體)。
    • auto_ptr:標準庫模板,自動管理動態分配的單個對象,防止記憶體洩漏。支持所有權概念。
    • 數組分配:new type[size]delete [] pointer 釋放。
    • 常量對象:可在空閒儲存區創建 const 對象。
    • 定位 newnew (address) type 在指定記憶體位址構造對象。無對應 delete 操作符。
  6. 名字空間定義: namespace 封裝名字,解決全域名字空間污染。定義 (namespace name { ... }) 可非連續。名字空間成員使用合格名字 (namespace_name::member_name) 引用。嵌套名字空間。定義可在名字空間定義之外(需合格名字)。ODR 適用於名字空間成員。未命名名字空間 (namespace { ... }) 定義局部於檔案的實體。
  7. 使用名字空間成員: namespace alias 為名字空間提供別名。using declaration (using namespace_name::member_name) 使特定名字空間成員在特定作用域可見。using directive (using namespace namespace_name) 使整個名字空間的成員在特定作用域可見。std 名字空間及其使用(建議使用 using 宣告)。

第九章 重載函式:使用同一個名字實現不同行為

本章詳細介紹了 C++ 中的函式重載機制:

  1. 重載函式宣告: 允許兩個或更多函式共享同一個名字,只要參數列表不同。參數列表(數量或類型)是函式的標誌。返回類型不能區分重載函式。 constvolatile 對指標/引用的影響可區分。typedef 不能區分。作用域和重載:同一作用域中的同名函式才能重載。using 宣告/指示符可將基類或名字空間的函式引入作用域並形成重載集。extern "C" 函式不能重載。
  2. 重載解析的步驟: function overload resolution 是將函式呼叫與重載函式集中的一個函式匹配的過程。
    • 確定候選函式 (candidate function):同名且在呼叫點可見的函式,包括與實參類型相關的名字空間中的函式和友元函式。
    • 確定可行函式 (viable function):能用呼叫中指定的實參呼叫的候選函式(參數數量匹配,實參可轉換為參數類型)。
    • 選擇最佳可行函式 (best viable function):根據實參類型轉換的等級選擇最佳匹配的函式。
  3. 參數類型轉換: 實參可轉換為函式參數類型,轉換分級影響重載解析。
    • 精確匹配 (exact match):類型完全匹配或只需要最小轉換(左值轉右值、數組轉指標、函式轉指標、限定修飾符轉換)。
    • 提升 (promotion):小整數類型、float、枚舉、布林類型提升到更大或標準類型。
    • 標準轉換 (standard conversion):其他整數/浮點/浮點-整數轉換、指標轉換(0 到指標、任何指標到 void*)、布林轉換。
    • 引用參數:實參必須是引用的有效初始化值才可行。
  4. 重載解析細節: 詳細闡述三步驟過程。候補函式來源。可行函式判斷(參數數量、類型轉換)。最佳可行函式選擇(比較實參轉換序列等級)。函式實參上的轉換序列 구성 (左值轉換、提升/標準轉換、限定修飾符轉換)。比較兩個轉換序列等級。缺省實參如何影響可行函式。

第十章 函式模板:程式碼的通用化

本章介紹函式模板,實現程式碼的通用化:

  1. 函式模板定義: template <parameter list> 定義通用的函式算法,參數列表可包含類型參數 (class Typetypename Type) 和非類型參數 (int size 等常量表達式)。函式主體使用這些參數作為佔位符。
  2. 函式模板實例化: 編譯器根據函式呼叫或取址時提供的實際類型/值,從模板定義自動生成函式實例 (template instantiation)。這個過程是隱式的。point of instantiation
  3. 模板實參推演: 編譯器根據函式實參的類型推斷模板實參的類型和值。允許的實參轉換有限制(左值轉換、限定修飾符轉換、派生類轉基類)。
  4. 顯式模板實參: 在某些情況下推演可能失敗或期望特定實例,可顯式指定模板實參 (template <Args> function(args)function<Args>(args))。
  5. 模板編譯模式: 如何組織程式碼以定義和使用模板。
    • 包含模式 (inclusion model):模板定義放在頭文件中,在使用實例的文件中包含頭文件。
    • 分離模式 (separation model):宣告放頭文件,定義放實現文件。export 關鍵字標記定義(部分編譯器支持)。
    • 顯式實例宣告 (explicit instantiation declaration):template function<Args>(); 要求編譯器在指定位置實例化模板。
  6. 模板顯式特化: template<> return_type function<Args>(params) { ... } 為特定模板實參集合提供不同的實現,覆蓋通用模板定義生成的實例。
  7. 重載函式模板: 函式模板可以重載。不同函式模板可有相同名稱,只要參數列表不同。
  8. 重載解析與模板函式實例: 函式呼叫可能匹配普通函式或函式模板的實例。重載解析過程會考慮模板實例,根據實參轉換等級選擇最佳匹配。如果通用模板和特化都匹配,特化優先。如果普通函式和模板實例都匹配,普通函式優先。
  9. 模板定義中的名字解析: 分兩階段進行。不依賴模板參數的名字在模板定義時解析。依賴模板參數的名字在模板實例化時解析。
  10. 名字空間與函式模板: 函式模板可在名字空間中定義。特化宣告需在名字空間內。

第四篇:基於物件的程式設計 (Chapters 13-16)

這部分詳細介紹了 C++ 的類機制及其相關特性,以支援抽象資料類型和物件的創建與使用。

第十三章 類:定義自己的資料類型

本章介紹類作為定義使用者自訂類型的方式:

  1. 類定義: class ClassName { member-list }; 類頭與類體。類定義引入一個獨立的類類型和類域 (class scope)。
  2. 資料成員: 類中儲存資料的變數。可為任意類型。不可在類體中直接初始化(除了 const static 整數類型)。
  3. 成員函式: 類中定義的操作。宣告在類體中,定義可在類內或類外(需使用類域操作符 ClassName::)。成員函式具備訪問類的私有成員的特權。可重載。const 成員函式不修改資料成員。volatile 成員函式。特殊成員函式(構造、析構、賦值、轉換)。
  4. 成員訪問: 使用 . (對象/引用) 或 -> (指標) 操作符訪問成員。
  5. 資訊隱藏: public, private, protected 訪問控制。private 成員只能由成員和友元訪問,protected 成員由成員、友元和派生類訪問,public 成員可在任何地方訪問。通常資料成員設為私有,成員函式設為公有。
  6. 友元: friend 關鍵字授予函式或類訪問另一個類私有成員的權限。可以是全域函式、其他類的成員函式或整個類。友元不是成員,不受訪問控制影響。常用於重載操作符。
  7. 類宣告與定義: 類可只宣告 (class ClassName;),但不定義。只宣告的類只能用於聲明指標或引用。完整定義提供成員和大小信息。
  8. 類對象: 定義類對象才分配記憶體。每個對象有自己的資料成員拷貝。對象有域和生命期。可互相初始化/賦值。可聲明指標和引用。通過成員訪問操作符訪問成員。
  9. this 指標: 每個非靜態成員函式都隱含一個 this 指標,指向呼叫該函式的對象。用於訪問當前對象的成員。可顯式使用。常用於讓成員函式返回對象自身的引用(支援鏈式呼叫)或比較對象地址(防止自我賦值)。
  10. 靜態類成員: static 資料成員所有對象共享一份拷貝。static 成員函式不操作特定對象,沒有 this 指標,只能訪問靜態成員。通過類名或對象/指標訪問靜態成員。
  11. 指向類成員的指標: 指向非靜態資料成員 (Type ClassName::*) 或非靜態成員函式 (ReturnType (ClassName::* PointerName)(Params)) 的指標。與普通指標不同,需要綁定到特定對象才能訪問。靜態成員地址是普通指標。
  12. 聯合: union 節省空間的類,成員共用記憶體。同一時間只有一個成員有值。匿名聯合。誤用危險性。
  13. 位域: bit-field 節省空間的資料成員,儲存特定位數。

第十四章 類的初始化 賦值和析構:對象的生命始末

本章詳細介紹類對象生命周期開始和結束時的自動行為:

  1. 類初始化: 類對象首次使用前自動初始化。公共資料成員類可使用顯式初始化列表 {}(C 風格)。構造函式 (constructor) 是用於初始化的特殊成員函式,與類同名,無返回值,可重載。缺省構造函式 (default constructor) 無需實參。explicit 關鍵字抑制單參數構造函式的隱式轉換。構造函式可設為私有限制創建。
  2. 成員初始化表: ClassName() : member(value), ... { ... } 在構造函式原型後用冒號分隔成員和初始值列表。用於顯式初始化成員類對象或 const/引用成員。成員初始化順序由宣告順序決定。
  3. 拷貝構造函式: ClassName(const ClassName&) 用同類對象初始化新對象。預設是按成員初始化 (default memberwise initialization)。對於包含指標或需要唯一性(如帳號)的成員,預設行為可能不夠,需提供顯式拷貝構造函式實現正確語義(如深拷貝或生成新 ID)。拷貝構造函式通常是 const 引用的參數。不提供顯式定義可防止拷貝。
  4. 析構函式: ~ClassName() 對象生命期結束時自動呼叫。無返回類型、無參數。用於釋放資源(如 new 分配的記憶體、互斥鎖)。只有需要釋放資源的類才需要析構函式。可顯式呼叫析構函式(常與定位 new 結合)。
  5. 類對象數組和 vector 數組元素用缺省構造函式初始化。可用初始化列表提供實參。動態分配數組 (new type[size]) 要求類有缺省構造函式,元素用缺省構造函式初始化。delete [] pointer 釋放數組(需 [] 確保呼叫每個元素的析構函式)。堆上數組初始化可結合定位 newvector 的元素初始化也涉及構造和拷貝構造函式,效率考量。
  6. 按成員賦值: ClassName& operator=(const ClassName&) 對象賦值。預設行為是按成員賦值 (default memberwise assignment),涉及隱含的拷貝賦值操作符。對包含指標或需要唯一性的成員,預設行為不足,需提供顯式拷貝賦值操作符,通常要檢查自我賦值 (this != &rhs)。可設為私有防止賦值。
  7. 效率問題: 通過指標/引用傳遞對象通常比傳值高效。返回指標/引用也比按值返回高效。命名返回值最佳化 (named return value optimization) 可能將按值返回轉換為按引用返回。初始化通常比賦值更高效。

第十五章 重載操作符和使用者定義的轉換:擴充語言表達能力

本章介紹重載操作符和使用者定義的轉換,使類型的使用更靈活:

  1. 操作符重載: 為類類型定義預定義操作符的行為。使用 operator symbol 作為函式名。可為成員函式或非成員函式。非成員操作符需要顯式參數代表操作數。
  2. 成員 vs. 非成員: 特定操作符必須是成員(=, [], (), ->, new, delete)。非成員操作符常用於對稱操作(如 ==),可解決成員操作符左操作數必須是類類型限制。非成員操作符常需設為友元訪問私有成員。
  3. 操作符=: 除了拷貝賦值,可重載接受其他類型參數的 =
  4. 操作符[]: 為容器類定義下標訪問。返回類型常為引用(支援左值)。
  5. 操作符(): 重載函式呼叫操作符,用於函式對象。
  6. 操作符->: 重載成員訪問箭頭操作符,用於智慧指標等。
  7. 操作符++和–: 重載遞增/遞減操作符。需定義前置和後置版本(後置版本有額外 int 參數)。
  8. 操作符newdelete 重載全域 new()/delete() 或定義類成員 operator new()/operator delete() 接管特定類的記憶體管理。類成員版本需指定 size_t 參數。可重載定位 new。陣列版本 new[]/delete[]
  9. 使用者定義的轉換: 定義將類類型轉換為其他類型的方式。
    • 轉換函式 (operator type()):定義單向轉換(類 -> 其他類型)。無返回類型、無參數。
    • 構造函式作為轉換函式:單參數非顯式構造函式定義單向轉換(其他類型 -> 類)。explicit 關鍵字抑制隱式轉換。
  10. 選擇一個轉換: 當存在多個使用者定義轉換可行時,編譯器選擇最佳序列。根據轉換函數後的標準轉換等級或構造函式前的標準轉換等級比較。
  11. 重載解析和繼承: 繼承影響重載解析過程。
    • 候補函式:包含基類的成員、基類名字空間的函式、基類友元函式。
    • 可行函式和使用者定義轉換序列:考慮繼承來的轉換函數和構造函式。將派生類轉基類/指標/引用是標準轉換。
    • 最佳可行函式:繼承來的轉換會參與等級比較。派生類轉較近基類優於轉較遠基類。

第十六章 類模板:定義通用的資料類型

本章深入探討類模板,實現資料類型的通用化:

  1. 類模板定義: template <parameters> 定義通用類結構。參數可為類型 (class/typename) 或非類型(常量表達式)。成員使用這些參數。定義可在類外(需模板參數前綴)。
  2. 類模板實例化: 使用如 ClassName<Args> obj; 語法,編譯器根據 Args 從模板定義生成特定類實例。實例化在需要完整類定義的上下文發生。
  3. 模板實參推演: 編譯器無法從類模板實例名推斷參數,必須顯式指定 Args。與函式模板不同。
  4. 類模板的成員函式: 也是模板。定義可在類外。只在使用時才實例化。成員定義需 template <class ...> ClassName<class ...>::member<class ...>() 前綴。成員模板(函式或類)是類模板的成員。
  5. 類模板中的友元宣告: 可宣告非模板函式/類、綁定模板實例、非綁定模板為友元。通常需指定模板參數(如 friend class Queue<Type>;)。
  6. 類模板的靜態資料成員: 也是模板。每個類模板實例有自己的靜態成員拷貝。定義需在類外,指定模板參數前綴。僅在使用時實例化。
  7. 類模板的嵌套類型: 嵌套類可為模板。使用外圍類模板參數。嵌套類型使用需指定外圍類模板實例(如 Queue<int>::QueueItem*)。
  8. 類模板和編譯模式: 包含模式 (定義放頭文件,所有文件包含) vs. 分離模式 (宣告放頭文件,定義放實現文件,需 export)。顯式實例宣告控制實例化時機。
  9. 類模板特化: template<> class ClassName<Args> { ... } 為特定 Args 提供獨立定義。覆蓋通用模板。成員特化。部分特化 template <PartialArgs> class ClassName<Args> { ... } 為部分 Args 提供特化。
  10. 類模板中的名字解析: 兩階段解析。不依賴模板參數的名字定義時解析。依賴模板參數的名字實例化時解析。影響成員定義中的名字查找。
  11. 名字空間和類模板: 模板定義可在名字空間中。成員定義可名字空間外。特化宣告需在原模板名字空間內。

第五篇:物件導向的程式設計 (Chapters 17-19)

這部分深入探討物件導向的核心概念,特別是繼承、多態、虛擬函式、RTTI 和異常處理。

第十七章 類繼承和子類型:構建對象的層次結構

本章介紹繼承和子類型,作為物件導向的基礎:

  1. 繼承與子類型: 派生類從基類繼承資料和操作。class Derived : access Base { ... } 語法。派生類是基類的子類型 (subtype),反映 is-a 關係。基類指標/引用可指向派生類對象(多態)。
  2. 類繼承層次結構: 由基類和派生類組成。抽象基類 (abstract base class) 作為介面,不可實例化。具體基類 (concrete base class) 可實例化。
  3. 確定層次成員: 設計基類和派生類成員。公有介面通常在基類聲明為虛擬。保護成員 (protected) 供派生類訪問。數據成員和共享操作常提至基類。
  4. 基類和派生類的構造: 派生類對象包含基類子對象。構造函式呼叫順序:先基類,後派生類。派生類構造函式可通過成員初始化列表向基類構造函式傳參。派生類通常不能直接初始化基類資料成員。
  5. 析構函式: 呼叫順序:先派生類,後基類。通過基類指標刪除派生類對象時,基類析構函式必須是虛擬的 (virtual ~Base()),否則可能僅呼叫基類析構函式,導致資源洩漏。
  6. 虛擬函式: virtual 關鍵字標記,實現動態綁定。通過基類指標/引用呼叫虛擬函式時,根據對象實際類型在運行時決定呼叫哪個版本。非虛擬函式靜態綁定(編譯時決定)。純虛擬函式 (= 0) 標記抽象函式,使類成為抽象基類。在構造/析構函式中呼叫虛擬函式總是靜態呼叫基類版本。派生類虛擬函式需與基類版本原型匹配(返回類型可例外)。虛擬函式的缺省實參靜態綁定。虛擬 new 操作符不存在,常使用 clone() 虛擬函式模擬。
  7. 按成員初始化與賦值: 繼承下,預設按成員操作會拷貝基類子對象和派生類部分成員。若基類或派生類包含指標或需要獨特語義,預設行為不足,需顯式拷貝構造函式/賦值操作符。顯式版本需負責基類子對對象和派生部分成員的正確拷貝。
  8. UserQuery 管理類: 引入類將查詢處理邏輯封裝,管理 Query 類層次對象的創建與銷毀。

第十八章 多繼承和虛擬繼承:更複雜的類關係

本章探討多繼承和虛擬繼承,解決單繼承無法建模的問題:

  1. 多繼承: class Derived : access Base1, access Base2 { ... } 派生類從多個基類繼承。派生類包含每個基類的子對象。構造順序依派生列表。析構順序相反。基類成員直接訪問可能因同名而二義。派生類指標/引用轉為基類指標/引用。虛擬函式在多繼承下行為。按成員初始化/賦值。
  2. 虛擬繼承: class Derived : virtual access Base { ... } 解決多繼承中同一基類多次出現導致子對象重複和二義性問題。虛擬基類在最終派生類中只有一個共享實例。初始化語義特殊:虛擬基類由最終派生類初始化。構造順序:先所有虛擬基類,後非虛擬基類,最後派生類。析構順序相反。虛擬基類成員的可見性。

第十九章 C++中繼承的用法:多態的應用與細節

本章探討多態的實際應用及其支持的語言特性:

  1. RTTI (Run-Time Type Identification): 運行時識別多態對象的實際類型。
    • dynamic_cast:安全向下轉換基類指標/引用為派生類指標/引用。轉換失敗指標為 nullptr,引用拋異常。
    • typeid:獲取對象的實際類型信息 (type_info 對象)。type_info::name() 返回類型名 C 風格字串。
    • RTTI 僅用於帶虛擬函式的類。
  2. 異常和繼承: 將異常定義為類層次結構。
    • 拋出派生類異常:可被基類 catch 捕獲。
    • catch 子句順序:派生類 catch 必須在基類 catch 之前。
    • 異常對象與虛擬函式:基類 catch 處理派生類異常時,需用引用才能呼叫派生類虛擬函式。
    • 棧展開與析構函式:異常拋出時,棧展開會自動呼叫局部對象析構函式,實現 RAII (Resource Acquisition Is Initialization)。
    • 異常規範:繼承下,派生類虛擬函式的異常規範不能比基類窄。
    • 函數 try 塊:在構造函式中捕獲成員初始化列表拋出的異常。
    • C++ 標準庫異常類層次:exception 為根,派生出 logic_errorruntime_error 等。
  3. 重載解析與繼承: 繼承影響函式重載解析過程。
    • 候補函式:基類成員、基類名字空間函式、基類友元函式也會加入。
    • 可行函式與使用者定義轉換序列:考慮繼承來的轉換函式/構造函式。派生類轉基類是標準轉換。
    • 最佳可行函式:繼承來的轉換會影響轉換序列等級比較。

第二十章 iostream 庫:輸入輸出的抽象與應用

本章詳細介紹標準函式庫的 iostream 庫,用於輸入輸出:

  1. iostream 概述: iostream 庫提供輸入輸出功能,基於多重繼承和虛擬繼承的類層次。包括標準流物件 cin, cout, cerr。主要通過重載的 >> (輸入) 和 << (輸出) 操作符進行操作。支持文件 I/O (fstream) 和字串流 (sstream)。支持 charwchar_t 字元類型。
  2. 輸出操作符 << 基本用法,可接受多種類型參數(內建、字串、複數、指標),可串聯。介紹 endl 等操縱符 (manipulator)。指針輸出默認十六進制。可重載以支持使用者定義類型輸出。非成員操作符重載常用於對稱性。
  3. 輸入操作符 >> 基本用法,可接受多種類型參數。可串聯。讀取時默認跳過空白字元。輸入錯誤會設置流的錯誤狀態。可重載以支持使用者定義類型輸入。
  4. 其他輸入操作: get() 讀取單個字元(包括空白),getline() 讀取一行,read() 讀取指定位元組數,gcount() 返回實際讀取字元數。putback(), unget(), peek()
  5. 檔案輸入輸出: ifstream (輸入), ofstream (輸出), fstream (輸入輸出)。構造函式指定檔名和模式(如 ios_base::in, out, app)。可使用 open()close() 成員函式。可檢查文件是否成功打開。seekg()/seekp() 重新定位文件位置,tellg()/tellp() 獲取當前位置。
  6. 條件狀態: 流對象維護狀態標誌 (如 eofbit, badbit, failbit, goodbit)。eof(), bad(), fail(), good() 查詢狀態。setstate() 設置狀態,clear() 清除狀態。rdstate() 獲取狀態。
  7. 字串流: istringstream 從字串讀,ostringstream 寫到字串,stringstream 雙向。str() 成員函式獲取底層字串。用於在內存中格式化或解析字串。
  8. 格式狀態: 流對象的格式狀態控制輸出細節(进制、精度、對齊)。通過操縱符修改狀態。boolalpha, hex, oct, dec, showbase, showpoint, scientific, fixed, uppercase 控制格式。setprecision(), setw(), setfill(), setbase() 控制數值寬度和精度。
  9. 強類型庫: iostream 庫是強類型,編譯器檢查確保正確使用輸入輸出操作符。