Stanley Lippman:c++ Primer (中文版 第3版 潘爱民译)
C++ Primer 第三版 核心觀念闡述
本文件旨在提取並詳盡解釋《C++ Primer 第三版》一書(包含譯序及前言至第二十章)中的主要論點與觀念。
譯序與前言:理解 C++ 的演進與本書的特色
譯序和前言部分共同為讀者建立了理解 C++ 語言及其本書價值觀念的基礎。核心論點包括:
- C++ 的成熟與標準化: C++ 是一門有近 20 年歷史的傳統語言,但也因 1998 年完成 ISO 標準化而成為一門「新」語言。標準化帶來了語言特性的統一、標準函式庫的建立以及更好的程式碼可移植性。本書正是基於這個新標準來闡述 C++,強調不應再將 C++ 簡單地視為 C 語言的超集。
- 標準函式庫的重要性: 標準 C++ 引入了覆蓋面極廣的標準函式庫,特別是以前稱為 STL(Standard Template Library)的部分,包含了容器(如 vector, list, map, set)和泛型演算法。這大大豐富了 C++ 的基礎設施。
- C++ 對多重程式設計範式的支援: C++ 不僅支援物件導向(Object-Oriented)程式設計,也支援程序式(Procedural)和基於物件(Object-Based,即抽象資料類型)的程式設計。這種多範式支援使得 C++ 能夠為不同的問題提供最適合的解決方案,但也增加了語言的複雜性。
- 本書的權威性與深度: 本書由 C++ 領域的專家和標準委員會成員撰寫,結合了實作經驗與標準規範,具有權威性。儘管名為 “Primer”,本書內容深入,涵蓋了許多 C++ 的複雜特性,如函式重載解析、模板機制、異常處理等。因此,本書並非輕量的入門讀物,需要讀者投入時間反覆閱讀和練習。
- 本書的結構與特色: 本書結構清晰,圍繞一系列可擴展的範例來介紹語言特性,這有助於讀者理解特性背後的動機和實際用法。特色包括:內容廣泛、提供大量實用範例、敘述安排體現多範式特點、與編譯器無關且遵從標準、提供配套練習。
第一篇:C++ 概述 (Chapters 1-2)
這部分提供了對 C++ 全貌的快速瀏覽,介紹了問題解決的基本策略以及 C++ 語言的一些核心概念和機制。
第一章 開始:程式的基礎構成
本章從解決問題的方法(分而治之、逐步求精)入手,介紹了 C++ 程式的基礎構成元素:
- 程式的基本單位: 程式由陳述句 (statement) 組成,最簡單的是空陳述句。表達式 (expression) 是動作,以分號結尾的表達式可構成陳述句(如宣告、賦值、輸出)。
-
函式與 main(): 函式 (function) 是將陳述句按邏輯語義分組的命名單元。每個 C++ 程式必須包含
main()函式,它是程式執行時的第一個函式。函式由返回類型、函式名、參數表和函式體組成,可使用return陳述句返回值。 - 編譯與執行: C++ 程式的建立涉及原始程式碼檔案(通常有 .c, .C, .cxx, .cpp 等後綴)的編寫、預處理 (preprocessing)、編譯 (compilation)、連結 (linking) 生成可執行檔。編譯器會檢查語法錯誤和類型錯誤。
- 基本資料類型: 介紹了 C++ 內建的算術類型 (char, int, short, long, float, double, long double) 和布林類型 (bool),以及它們的有符號/無符號、長度差異。
-
標準函式庫基礎: 初步引入標準函式庫提供的抽象類型,如
string和vector,並指出這些類型也是基於 C++ 的類機制實現的。 -
流程控制: 介紹了
if陳述句用於條件執行,以及while和for迴圈用於重複執行。 -
預處理器指令: 介紹
#include用於包含頭文件,#ifndef/#define/#endif用於防止頭文件重複包含,#ifdef用於條件包含程式碼。提及預定義的預處理器宏和 C 標準庫的assert()宏。 -
註解: 介紹
/* */和//兩種註解形式及其用法,強調註解的有效性。 -
基本輸入/輸出: 介紹
iostream庫,預定義的cin,cout,cerr物件,以及<<(輸出) 和>>(輸入) 操作符,endl操縱符,並初步展示文件輸入輸出 (fstream)。
第二章 C++瀏覽:多範式與核心機制預覽
本章通過一個具體的範例(數組抽象的演進)快速預覽了 C++ 對不同程式設計範式的支援以及幾個核心機制:
-
內建數組的限制: 介紹內建數組作為同類型元素的集合,使用下標
[]訪問元素。指出其限制,如不支援整體操作(賦值、比較)、缺乏自我意識(大小未知)以及作為函式參數時的限制。 -
動態記憶體與指標: 簡要介紹動態記憶體分配 (dynamic memory allocation) 的概念(使用
new和delete),以及指標 (pointer) 作為存放記憶體地址的類型,用於間接操作對象,特別是動態分配的對象。 -
基於物件的設計 (類): 引入類 (class) 機制作為支援抽象資料類型的方式。介紹類定義 (class head, class body, public/private 訪問控制)、成員函式 (member function / method) 和資料成員 (data member)。通過
IntArray類展示如何將操作(賦值、比較、查找、排序等)和數據封裝在一起,實現資訊隱藏 (information hiding),並利用內聯函式 (inline function) 提高效能。介紹構造函式 (constructor) 作為自動初始化的機制和函式重載 (function overloading)。 -
物件導向的設計 (繼承與虛擬函式): 在基於物件設計的基礎上,引入繼承 (inheritance) 和動態綁定 (dynamic binding) 機制,支援物件導向。通過
IntArray派生IntArrayRC(帶範圍檢查) 展示繼承如何實現程式碼重用。介紹基類 (base class) 和派生類 (derived class / subtype) 的關係 (is-a),保護 (protected) 訪問級別。引入虛擬函式 (virtual function) 實現動態綁定,使得通過基類指標/引用呼叫方法時,實際執行的函式根據對象的實際類型在執行時決定。 -
泛型設計 (模板): 引入模板 (template) 機制,允許將類型或值參數化。通過將
IntArray泛化為Array模板,展示如何自動生成處理不同類型元素的類實例。介紹函式模板 (swap) 作為自動生成處理不同類型參數的函式的方式。 -
異常處理: 初步介紹異常 (exception) 作為運行時不正常情況的表示,以及
try,throw,catch關鍵字構成的異常處理機制。 -
名字空間: 介紹名字空間 (namespace) 機制,用來封裝名字,避免全域名字衝突,特別是標準函式庫中的名字被放在
std名字空間中。介紹合格名字 (qualified name) 和using指示符/宣告。 -
標準函式庫容器與演算法: 再次提及
vector作為標準庫提供的數組抽象(相對於內建數組的優勢),並首次介紹泛型演算法 (generic algorithm) 可以操作容器元素,如sort,find。提及map關聯容器。
第二篇:基本語言 (Chapters 3-6)
這部分深入探討了 C++ 程式的基本構成要素:資料類型、表達式和陳述句。
第三章 C++資料類型:程式碼的基石
本章詳細介紹了 C++ 支援的各種資料類型:
-
資料類型總覽: 強調資料類型決定了記憶體大小、值範圍及可應用操作。介紹內建數值類型、字元類型、布林類型,以及標準函式庫的
string、complex、vector和pair等抽象類型。 - 文字常量: 詳細介紹各種類型文字常量的寫法、類型後綴、轉義序列,並指出字符串文字是常量字元數組。
- 變數: 定義變數為具名記憶體儲存區,具備類型、值和地址(左值/右值)。區分宣告 (declaration) 與定義 (definition)。討論變數命名規則、初始化方式(顯式賦值、括號初始化、預設初始化、成員初始化列表),並指出未初始化對象的危險性。
-
指標類型: 詳細介紹指標作為存放地址的類型,指標的類型決定了對記憶體內容的解釋方式。
void*作為通用指標。解引用操作符*訪問指標指向的對象。指標算術運算及其在數組遍歷中的應用。 -
字串類型: 再次討論 C++ 的兩種字串表示:C 風格字串(
char*和 C 庫函式)和標準庫string類型(作為物件導向抽象的優勢、常用操作:大小、空判斷、賦值、比較、連接、查找、子字串、替換、c_str()轉換)。 -
const限定修飾符: 介紹const使對象成為常量(只讀),必須初始化。const對指標和引用的影響(指向const的指標 vs.const指針),在函式參數中的應用。 - 引用類型: 介紹引用作為對象的別名,必須初始化且不能重新綁定。引用作為函式參數(傳遞大型對象、返回多個值)的常用場景。區別引用與指標。
-
布林類型:
true和false文字常量。與整數類型的自動轉換。 -
枚舉類型:
enum定義具名整數常量的集合,每個枚舉定義一個獨立類型,限制值的有效範圍。 - 數組類型: 再次詳細介紹內建數組的定義、初始化、索引(從 0 開始)、多維數組。指出其不支持整體操作和缺乏自我意識的限制。數組名可被解釋為指向首元素的指標,引入數組與指標的關係。
-
vector容器類型: 再次介紹vector作為內建數組的替代,提供更豐富的操作(大小、空判斷、插入、刪除、與其他vector的初始化/賦值),支持動態增長。介紹數組習慣和 STL 習慣兩種用法。 -
complex類型: 標準庫複數類型,支持算術操作和 I/O。 -
typedef名稱: 為已有類型提供別名,提高可讀性,簡化複雜宣告。 -
volatile限定修飾符: 指出對象的值可能在編譯器控制外改變,影響編譯器優化。 -
pair類型: 標準庫類型,在單一對象中組合兩個值。 -
類類型: 再次引入類作為定義新類型的方式,通過
String類初步展示類成員、操作符重載、構造函式、析構函式等概念。
第四章 表達式:如何運算與控制流程細節
本章深入探討 C++ 的表達式以及如何組合操作符和控制運算流程:
- 表達式構成: 表達式由操作數 (operand) 和操作符 (operator) 組成,單元或複合。
-
算術操作符:
+,-,*,/,%。整數除法的截斷,求餘 (%) 的限制與符號問題。算術異常(溢出、下溢)可能導致未定義行為。 -
等於、關係、邏輯操作符:
==,!=,<,>,<=,>=返回布林值。!,&&,||邏輯操作符。&&和||的短路求值特性及其應用。關係操作符的潛在陷阱(運算順序未定義、鏈式比較)。 -
賦值操作符:
=將右值賦給左值。+=,-=, 等複合賦值操作符。賦值操作符的左操作數必須是左值。賦值表達式本身有返回值。 -
遞增與遞減操作符:
++,--用於加一或減一。前置 (++var) 和後置 (var++) 形式的區別及其在棧實現中的應用示例。 -
複數操作: 標準庫
complex類型支持內建算術操作符、複合賦值操作符、I/O 操作符等,可與內建類型混合運算。 -
條件操作符:
expr1 ? expr2 : expr3作為簡單if-else的替代。 -
sizeof操作符: 返回對象或類型名的位元組數。結果是size_t類型。用於數組時返回整個數組大小。在編譯時計算,是常量表達式。 -
new和delete表達式: 動態記憶體分配與釋放。new type(value)分配並初始化單個對象。new type[size]分配數組(內建類型不初始化)。delete pointer釋放單個對象。delete [] pointer釋放數組。動態分配對象是無名的。忘記釋放導致記憶體洩漏。誤用delete的危險。 -
逗號操作符:
,串聯表達式,從左到右求值,結果是最右邊表達式的值。 -
位操作符:
~,<<,>>,&,^,|。操作數被解釋為位集合。與布林邏輯操作符的區別。 -
bitset操作: 標準庫bitset類提供位集合抽象,相比直接操作整數位更安全、易用。提供設置、測試、翻轉、計數、大小等操作。 - 操作符優先級: 決定複合表達式中操作的求值順序。括號可改變優先級。關聯性決定相同優先級的操作符求值順序。
-
類型轉換: 發生在混合類型表達式、賦值、函式參數/返回值等場景。
- 隱式類型轉換:編譯器自動進行的標準轉換。
- 算術轉換:提升操作數至共同類型以防止精度損失。
- 顯式類型轉換:使用
static_cast,const_cast,dynamic_cast,reinterpret_cast強制轉換,可能繞過類型檢查,存在危險。 - 舊式強制類型轉換:C 語言風格的
(type)expr或 C++ 風格的type(expr),功能更廣泛但也更危險、可讀性差。
第五章 陳述句:程式的流程控制
本章詳細介紹了 C++ 中控制程式執行流程的陳述句:
-
簡單與複合陳述句: 單個陳述句以分號結束。複合陳述句
{}包圍多個陳述句,形成一個區塊 (block),可替代單個陳述句。空陳述句;和空複合陳述句{}。 -
宣告陳述句: 變數定義 (
int ival;) 可出現在任何允許陳述句的地方,實現宣告的局部性 (locality of declaration)。在控制結構(if,switch,for,while)條件中宣告變數。 -
if陳述句: 條件執行。if (condition) statement和if (condition) statement1 else statement2。condition可為表達式或宣告。空懸else問題及其解決方法。if-else鏈。 -
switch陳述句: 從一組互斥常量值中選擇執行路徑。case標籤必須是常量表達式。break陳述句跳出switch。省略break導致穿透 (fallthrough)。default標籤處理所有不匹配的情況。陷阱:忘記break、case值非法或重複、在case中宣告未用{}包圍。 -
for迴圈陳述句: 常見用於遍歷固定長度數據結構。包含初始化、條件、表達式三部分。可省略部分或全部。初始化部分可宣告變數,其作用域通常僅限於for迴圈及其嵌套區塊。 -
while迴圈陳述句: 當條件為真時重複執行。條件可為表達式或宣告。 -
do-while迴圈陳述句: 至少執行一次,然後再測試條件。條件只能是表達式。 -
break陳述句: 跳出最內層的while,do-while,for或switch陳述句。 -
continue陳述句: 跳過當前迴圈迭代的剩餘部分,開始下一次迭代。僅用於迴圈陳述句。 -
goto陳述句: 無條件跳轉到同一函式內的標號 (label)。不鼓勵使用,使程式碼流程難以理解和維護。不能跳過未用 {} 包圍的宣告。 -
鏈表範例: 通過實現單向鏈表類 (
ilist和ilist_item) 練習陳述句的用法,並逐步引入成員函式、構造/析構函式、指標、引用、操作符重載等概念。
第六章 抽象容器類型:處理集合數據
本章聚焦於標準函式庫提供的容器類型,特別是序列容器和關聯容器:
-
容器概述: 容器是處理集合數據的抽象。序列容器(
vector,list,deque)和關聯容器(map,set,multimap,multiset)。 -
vectorvs.list: 比較兩種序列容器的優劣勢(隨機訪問、插入/刪除效率、記憶體連續性),提供選擇容器的準則。 -
vector的動態增長:vector通過預留 (reserve) 容量和重新分配 (reallocation) 實現高效增長,而非每次插入都重新分配。討論容量 (capacity) 與長度 (size) 的區別,不同類型元素對增長效能的影響。reserve()成員函式。 -
定義序列容器: 使用
<vector>,<list>,<deque>頭文件。介紹各種初始化方式(空容器、指定長度、指定長度並賦初值、用迭代器對或指標對初始化、用另一個容器初始化)。resize()改變長度。容器之間關係操作符(比較)。容器元素類型必須滿足的要求(相等、小於、預設值)。 -
迭代器: 迭代器 (iterator) 提供通用遍歷容器的方式。
begin()指向首元素,end()指向尾後位置。++前進,*解引用。const_iterator用於遍歷常量容器。迭代器算術僅適用於隨機訪問迭代器。迭代器對可標記範圍,用於初始化或傳遞給演算法。istream_iterator和ostream_iterator用於流。 -
序列容器操作:
push_back(),push_front(),insert()(在指定位置插入單個/多個/範圍元素)。erase()(刪除單個/範圍元素),pop_back(),pop_front()(刪除首尾元素)。賦值 (=) 和交換 (swap()) 操作。 -
泛型演算法: 再次強調泛型演算法 (generic algorithm) 獨立於容器類型,通過迭代器對操作元素。許多容器操作(如
find,sort)是作為泛型演算法提供的。 -
文字查詢系統範例 (部分): 通過實現文字查詢系統逐步展示容器用法。
- 儲存文本行:使用
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()(替換子字串)。
- 儲存文本行:使用
-
map關聯容器: 介紹map作為鍵/值對集合,通過鍵索引值,元素有序。用於建立單詞位置映射表(單詞作為鍵,位置向量作為值)。定義map(map<Key, Value>), 插入元素(下標操作符 vs.insert()), 查找 (count(),find()),遍歷map(迭代器指向pair物件),刪除 (erase())。 -
建立單詞排除集 (
set): 介紹set作為具備唯一鍵值的集合,用於判斷值是否存在,元素有序。用於建立排除無意義單詞的集合。定義set(set<Type>), 插入元素 (insert()), 查找 (count(),find()), 遍歷set。inserter迭代器适配器用於插入元素。 -
multimap和multiset: 介紹multimap和multiset允許鍵重複,與map和set相似。訪問重複鍵的元素需要迭代器 (find()結合count()或equal_range())。不支持下標操作符。 -
stack: 標準庫stack作為容器適配器,預設由deque實現,支援後進先出 (push,pop,top,size,empty)。 -
queue和priority_queue:queue支援先進先出 (push,pop,front,back,size,empty)。priority_queue支援按優先級獲取元素。
第三篇:基於過程的程式設計 (Chapters 7-12)
這部分詳細探討了 C++ 中如何將基本語言元素組織成函式,以及相關的機制和特性。
第七章 函式:程式碼的組織單元
本章詳細介紹了函式的定義、宣告和使用:
-
函式概述: 函式是程式碼的組織單元,包含一組實現特定任務的陳述句。函式由返回類型、名稱、參數列表和主體組成。
main()是程式入口。函式呼叫 (invocation) 涉及參數傳遞。函式需要先宣告 (declaration) 後使用。函式宣告(原型function prototype)描述介面。 -
函式原型: 返回類型(可為大多數類型,不可為數組或函式類型)、參數列表(逗號分隔的參數類型,可選參數名,空參數列表可用
void表示)。參數類型檢查 (strong type-checked)。 -
參數傳遞: 參數傳遞方式決定了函式如何接收實參 (argument)。
- 按值傳遞 (pass-by-value):預設方式,傳遞實參值的拷貝。函式內修改不影響實參。
- 指標參數:傳遞實參地址,函式可間接修改實參。
- 引用參數 (reference parameter / pass-by-reference):傳遞實參的左值,函式可直接修改實參。常用於傳遞大型對象或返回多個值。
const引用參數用於傳遞大型對象同時保證不修改。指標引用。 - 數組參數:數組永遠不按值傳遞,實質是傳遞指向首元素的指標。函式內修改影響原始數組。函式不知數組長度,常需額外參數。數組的引用參數包含數組長度資訊。
- 抽象容器類型參數:容器可按值或按引用傳遞。按引用傳遞效率更高。
- 預設實參 (default argument):為參數指定預設值,呼叫時可省略相應實參。預設實參必須從右邊開始指定。
- 省略號 (ellipsis):
...暫停類型檢查,允許傳遞未知數目和類型的實參。
-
返回值:
return陳述句結束函式並可返回值。返回void表示無返回值。返回值按值傳遞(預設)。可返回指標或引用。返回局部對象的引用是錯誤的。返回值也可以用作左值。函式參數和返回值 vs. 全域對象:建議通過參數傳遞而非全域對象進行函式間通信。 - 遞歸: 函式直接或間接呼叫自身。必須有停止條件。
-
內聯函式:
inline關鍵字建議編譯器將函式體在呼叫點展開,消除函式呼叫開銷,用於優化小型頻繁呼叫函式。只是建議,非保證。內聯函式定義通常放在頭文件中。 -
連結指示符:
extern "C"等用於指定與其他語言編寫的函式(如 C 函式)的連結規則。可為單一陳述句或複合陳述句。 -
main(): 程式的入口。可接收命令行參數int argc, char *argv[]。 -
指向函式的指標: 指標可指向函式。宣告語法(
返回類型 (*指標名)(參數列表))。初始化與賦值。通過指標呼叫函式。函式指標的數組。用作函式參數或返回值。指向extern "C"函式的指標。
第八章 域和生命期:名字的有效範圍與對象的存活時間
本章探討名字在程式碼中的可見範圍(域)以及對象在運行時的存活時間(生命期):
- 域: C++ 中的每個名字都指向唯一的實體,域是名字的語義上下文。包括全域域 (global scope / global namespace scope)、局部域 (local scope / function block)、類域 (class scope)、名字空間域 (namespace scope)。名字解析 (name resolution) 是將表達式中的名字與宣告關聯的過程。
-
局部域: 函式定義或複合陳述句內的區域。可嵌套。名字解析從內向外查找。在控制結構(
for,if,switch,while)條件中宣告的變數作用域。 -
全域對象和函式: 在全域域宣告的對象 (global object) 和函式 (global function)。程式整個執行過程中存在。全域對象/非內聯函式在程式中只能定義一次 (ODR, One Definition Rule)。宣告 vs. 定義 (
extern關鍵字)。不同文件間宣告的匹配。類型安全連結 (type-safe linkage)。頭文件的作用(局部性、一致性)。 - 局部對象: 在局部域宣告的對象。包括自動對象 (automatic object)、寄存器對象 (register object)、局部靜態對象 (local static object)。區別在於存儲區屬性與生命期。自動對象在函式呼叫時分配,結束時釋放(棧上)。寄存器對象是建議放在寄存器中的自動對象。局部靜態對象程式執行期間一直存在,首次執行到宣告點時初始化。未初始化自動對象值未指定,局部靜態對象初始化為 0。返回局部對象的地址是錯誤的(空懸指標)。
-
動態分配的對象: 使用
new和delete在空閒儲存區 (free store / heap) 分配和釋放的對象,生命期由程式員控制。可分配單個對象或數組。數組長度可在運行時確定。- 單個對象分配:
new type(value)分配並可初始化。返回指標。 - 釋放:
delete pointer。誤用delete的危險(記憶體洩漏、重複刪除、使用已釋放記憶體)。 -
auto_ptr:標準庫模板,自動管理動態分配的單個對象,防止記憶體洩漏。支持所有權概念。 - 數組分配:
new type[size]。delete [] pointer釋放。 - 常量對象:可在空閒儲存區創建
const對象。 - 定位
new:new (address) type在指定記憶體位址構造對象。無對應delete操作符。
- 單個對象分配:
-
名字空間定義:
namespace封裝名字,解決全域名字空間污染。定義 (namespace name { ... }) 可非連續。名字空間成員使用合格名字 (namespace_name::member_name) 引用。嵌套名字空間。定義可在名字空間定義之外(需合格名字)。ODR 適用於名字空間成員。未命名名字空間 (namespace { ... }) 定義局部於檔案的實體。 -
使用名字空間成員:
namespace alias為名字空間提供別名。using declaration(using namespace_name::member_name) 使特定名字空間成員在特定作用域可見。using directive(using namespace namespace_name) 使整個名字空間的成員在特定作用域可見。std名字空間及其使用(建議使用using宣告)。
第九章 重載函式:使用同一個名字實現不同行為
本章詳細介紹了 C++ 中的函式重載機制:
-
重載函式宣告: 允許兩個或更多函式共享同一個名字,只要參數列表不同。參數列表(數量或類型)是函式的標誌。返回類型不能區分重載函式。
const和volatile對指標/引用的影響可區分。typedef不能區分。作用域和重載:同一作用域中的同名函式才能重載。using宣告/指示符可將基類或名字空間的函式引入作用域並形成重載集。extern "C"函式不能重載。 -
重載解析的步驟:
function overload resolution是將函式呼叫與重載函式集中的一個函式匹配的過程。- 確定候選函式 (candidate function):同名且在呼叫點可見的函式,包括與實參類型相關的名字空間中的函式和友元函式。
- 確定可行函式 (viable function):能用呼叫中指定的實參呼叫的候選函式(參數數量匹配,實參可轉換為參數類型)。
- 選擇最佳可行函式 (best viable function):根據實參類型轉換的等級選擇最佳匹配的函式。
-
參數類型轉換: 實參可轉換為函式參數類型,轉換分級影響重載解析。
- 精確匹配 (exact match):類型完全匹配或只需要最小轉換(左值轉右值、數組轉指標、函式轉指標、限定修飾符轉換)。
- 提升 (promotion):小整數類型、
float、枚舉、布林類型提升到更大或標準類型。 - 標準轉換 (standard conversion):其他整數/浮點/浮點-整數轉換、指標轉換(0 到指標、任何指標到
void*)、布林轉換。 - 引用參數:實參必須是引用的有效初始化值才可行。
- 重載解析細節: 詳細闡述三步驟過程。候補函式來源。可行函式判斷(參數數量、類型轉換)。最佳可行函式選擇(比較實參轉換序列等級)。函式實參上的轉換序列 구성 (左值轉換、提升/標準轉換、限定修飾符轉換)。比較兩個轉換序列等級。缺省實參如何影響可行函式。
第十章 函式模板:程式碼的通用化
本章介紹函式模板,實現程式碼的通用化:
-
函式模板定義:
template <parameter list>定義通用的函式算法,參數列表可包含類型參數 (class Type或typename Type) 和非類型參數 (int size等常量表達式)。函式主體使用這些參數作為佔位符。 -
函式模板實例化: 編譯器根據函式呼叫或取址時提供的實際類型/值,從模板定義自動生成函式實例 (template instantiation)。這個過程是隱式的。
point of instantiation。 - 模板實參推演: 編譯器根據函式實參的類型推斷模板實參的類型和值。允許的實參轉換有限制(左值轉換、限定修飾符轉換、派生類轉基類)。
-
顯式模板實參: 在某些情況下推演可能失敗或期望特定實例,可顯式指定模板實參 (
template <Args> function(args)或function<Args>(args))。 -
模板編譯模式: 如何組織程式碼以定義和使用模板。
- 包含模式 (inclusion model):模板定義放在頭文件中,在使用實例的文件中包含頭文件。
- 分離模式 (separation model):宣告放頭文件,定義放實現文件。
export關鍵字標記定義(部分編譯器支持)。 - 顯式實例宣告 (explicit instantiation declaration):
template function<Args>();要求編譯器在指定位置實例化模板。
-
模板顯式特化:
template<> return_type function<Args>(params) { ... }為特定模板實參集合提供不同的實現,覆蓋通用模板定義生成的實例。 - 重載函式模板: 函式模板可以重載。不同函式模板可有相同名稱,只要參數列表不同。
- 重載解析與模板函式實例: 函式呼叫可能匹配普通函式或函式模板的實例。重載解析過程會考慮模板實例,根據實參轉換等級選擇最佳匹配。如果通用模板和特化都匹配,特化優先。如果普通函式和模板實例都匹配,普通函式優先。
- 模板定義中的名字解析: 分兩階段進行。不依賴模板參數的名字在模板定義時解析。依賴模板參數的名字在模板實例化時解析。
- 名字空間與函式模板: 函式模板可在名字空間中定義。特化宣告需在名字空間內。
第四篇:基於物件的程式設計 (Chapters 13-16)
這部分詳細介紹了 C++ 的類機制及其相關特性,以支援抽象資料類型和物件的創建與使用。
第十三章 類:定義自己的資料類型
本章介紹類作為定義使用者自訂類型的方式:
-
類定義:
class ClassName { member-list };類頭與類體。類定義引入一個獨立的類類型和類域 (class scope)。 -
資料成員: 類中儲存資料的變數。可為任意類型。不可在類體中直接初始化(除了
const static整數類型)。 -
成員函式: 類中定義的操作。宣告在類體中,定義可在類內或類外(需使用類域操作符
ClassName::)。成員函式具備訪問類的私有成員的特權。可重載。const成員函式不修改資料成員。volatile成員函式。特殊成員函式(構造、析構、賦值、轉換)。 -
成員訪問: 使用
.(對象/引用) 或->(指標) 操作符訪問成員。 -
資訊隱藏:
public,private,protected訪問控制。private成員只能由成員和友元訪問,protected成員由成員、友元和派生類訪問,public成員可在任何地方訪問。通常資料成員設為私有,成員函式設為公有。 -
友元:
friend關鍵字授予函式或類訪問另一個類私有成員的權限。可以是全域函式、其他類的成員函式或整個類。友元不是成員,不受訪問控制影響。常用於重載操作符。 -
類宣告與定義: 類可只宣告 (
class ClassName;),但不定義。只宣告的類只能用於聲明指標或引用。完整定義提供成員和大小信息。 - 類對象: 定義類對象才分配記憶體。每個對象有自己的資料成員拷貝。對象有域和生命期。可互相初始化/賦值。可聲明指標和引用。通過成員訪問操作符訪問成員。
-
this指標: 每個非靜態成員函式都隱含一個this指標,指向呼叫該函式的對象。用於訪問當前對象的成員。可顯式使用。常用於讓成員函式返回對象自身的引用(支援鏈式呼叫)或比較對象地址(防止自我賦值)。 -
靜態類成員:
static資料成員所有對象共享一份拷貝。static成員函式不操作特定對象,沒有this指標,只能訪問靜態成員。通過類名或對象/指標訪問靜態成員。 -
指向類成員的指標: 指向非靜態資料成員 (
Type ClassName::*) 或非靜態成員函式 (ReturnType (ClassName::* PointerName)(Params)) 的指標。與普通指標不同,需要綁定到特定對象才能訪問。靜態成員地址是普通指標。 -
聯合:
union節省空間的類,成員共用記憶體。同一時間只有一個成員有值。匿名聯合。誤用危險性。 -
位域:
bit-field節省空間的資料成員,儲存特定位數。
第十四章 類的初始化 賦值和析構:對象的生命始末
本章詳細介紹類對象生命周期開始和結束時的自動行為:
-
類初始化: 類對象首次使用前自動初始化。公共資料成員類可使用顯式初始化列表
{}(C 風格)。構造函式 (constructor) 是用於初始化的特殊成員函式,與類同名,無返回值,可重載。缺省構造函式 (default constructor) 無需實參。explicit關鍵字抑制單參數構造函式的隱式轉換。構造函式可設為私有限制創建。 -
成員初始化表:
ClassName() : member(value), ... { ... }在構造函式原型後用冒號分隔成員和初始值列表。用於顯式初始化成員類對象或const/引用成員。成員初始化順序由宣告順序決定。 -
拷貝構造函式:
ClassName(const ClassName&)用同類對象初始化新對象。預設是按成員初始化 (default memberwise initialization)。對於包含指標或需要唯一性(如帳號)的成員,預設行為可能不夠,需提供顯式拷貝構造函式實現正確語義(如深拷貝或生成新 ID)。拷貝構造函式通常是const引用的參數。不提供顯式定義可防止拷貝。 -
析構函式:
~ClassName()對象生命期結束時自動呼叫。無返回類型、無參數。用於釋放資源(如new分配的記憶體、互斥鎖)。只有需要釋放資源的類才需要析構函式。可顯式呼叫析構函式(常與定位new結合)。 -
類對象數組和
vector: 數組元素用缺省構造函式初始化。可用初始化列表提供實參。動態分配數組 (new type[size]) 要求類有缺省構造函式,元素用缺省構造函式初始化。delete [] pointer釋放數組(需[]確保呼叫每個元素的析構函式)。堆上數組初始化可結合定位new。vector的元素初始化也涉及構造和拷貝構造函式,效率考量。 -
按成員賦值:
ClassName& operator=(const ClassName&)對象賦值。預設行為是按成員賦值 (default memberwise assignment),涉及隱含的拷貝賦值操作符。對包含指標或需要唯一性的成員,預設行為不足,需提供顯式拷貝賦值操作符,通常要檢查自我賦值 (this != &rhs)。可設為私有防止賦值。 - 效率問題: 通過指標/引用傳遞對象通常比傳值高效。返回指標/引用也比按值返回高效。命名返回值最佳化 (named return value optimization) 可能將按值返回轉換為按引用返回。初始化通常比賦值更高效。
第十五章 重載操作符和使用者定義的轉換:擴充語言表達能力
本章介紹重載操作符和使用者定義的轉換,使類型的使用更靈活:
-
操作符重載: 為類類型定義預定義操作符的行為。使用
operator symbol作為函式名。可為成員函式或非成員函式。非成員操作符需要顯式參數代表操作數。 -
成員 vs. 非成員: 特定操作符必須是成員(
=,[],(),->,new,delete)。非成員操作符常用於對稱操作(如==),可解決成員操作符左操作數必須是類類型限制。非成員操作符常需設為友元訪問私有成員。 -
操作符=: 除了拷貝賦值,可重載接受其他類型參數的
=。 - 操作符[]: 為容器類定義下標訪問。返回類型常為引用(支援左值)。
- 操作符(): 重載函式呼叫操作符,用於函式對象。
- 操作符->: 重載成員訪問箭頭操作符,用於智慧指標等。
-
操作符++和–: 重載遞增/遞減操作符。需定義前置和後置版本(後置版本有額外
int參數)。 -
操作符
new和delete: 重載全域new()/delete()或定義類成員operator new()/operator delete()接管特定類的記憶體管理。類成員版本需指定size_t參數。可重載定位new。陣列版本new[]/delete[]。 -
使用者定義的轉換: 定義將類類型轉換為其他類型的方式。
- 轉換函式 (
operator type()):定義單向轉換(類 -> 其他類型)。無返回類型、無參數。 - 構造函式作為轉換函式:單參數非顯式構造函式定義單向轉換(其他類型 -> 類)。
explicit關鍵字抑制隱式轉換。
- 轉換函式 (
- 選擇一個轉換: 當存在多個使用者定義轉換可行時,編譯器選擇最佳序列。根據轉換函數後的標準轉換等級或構造函式前的標準轉換等級比較。
-
重載解析和繼承: 繼承影響重載解析過程。
- 候補函式:包含基類的成員、基類名字空間的函式、基類友元函式。
- 可行函式和使用者定義轉換序列:考慮繼承來的轉換函數和構造函式。將派生類轉基類/指標/引用是標準轉換。
- 最佳可行函式:繼承來的轉換會參與等級比較。派生類轉較近基類優於轉較遠基類。
第十六章 類模板:定義通用的資料類型
本章深入探討類模板,實現資料類型的通用化:
-
類模板定義:
template <parameters>定義通用類結構。參數可為類型 (class/typename) 或非類型(常量表達式)。成員使用這些參數。定義可在類外(需模板參數前綴)。 -
類模板實例化: 使用如
ClassName<Args> obj;語法,編譯器根據Args從模板定義生成特定類實例。實例化在需要完整類定義的上下文發生。 -
模板實參推演: 編譯器無法從類模板實例名推斷參數,必須顯式指定
Args。與函式模板不同。 -
類模板的成員函式: 也是模板。定義可在類外。只在使用時才實例化。成員定義需
template <class ...> ClassName<class ...>::member<class ...>()前綴。成員模板(函式或類)是類模板的成員。 -
類模板中的友元宣告: 可宣告非模板函式/類、綁定模板實例、非綁定模板為友元。通常需指定模板參數(如
friend class Queue<Type>;)。 - 類模板的靜態資料成員: 也是模板。每個類模板實例有自己的靜態成員拷貝。定義需在類外,指定模板參數前綴。僅在使用時實例化。
-
類模板的嵌套類型: 嵌套類可為模板。使用外圍類模板參數。嵌套類型使用需指定外圍類模板實例(如
Queue<int>::QueueItem*)。 -
類模板和編譯模式: 包含模式 (定義放頭文件,所有文件包含) vs. 分離模式 (宣告放頭文件,定義放實現文件,需
export)。顯式實例宣告控制實例化時機。 -
類模板特化:
template<> class ClassName<Args> { ... }為特定Args提供獨立定義。覆蓋通用模板。成員特化。部分特化template <PartialArgs> class ClassName<Args> { ... }為部分Args提供特化。 - 類模板中的名字解析: 兩階段解析。不依賴模板參數的名字定義時解析。依賴模板參數的名字實例化時解析。影響成員定義中的名字查找。
- 名字空間和類模板: 模板定義可在名字空間中。成員定義可名字空間外。特化宣告需在原模板名字空間內。
第五篇:物件導向的程式設計 (Chapters 17-19)
這部分深入探討物件導向的核心概念,特別是繼承、多態、虛擬函式、RTTI 和異常處理。
第十七章 類繼承和子類型:構建對象的層次結構
本章介紹繼承和子類型,作為物件導向的基礎:
-
繼承與子類型: 派生類從基類繼承資料和操作。
class Derived : access Base { ... }語法。派生類是基類的子類型 (subtype),反映is-a關係。基類指標/引用可指向派生類對象(多態)。 - 類繼承層次結構: 由基類和派生類組成。抽象基類 (abstract base class) 作為介面,不可實例化。具體基類 (concrete base class) 可實例化。
- 確定層次成員: 設計基類和派生類成員。公有介面通常在基類聲明為虛擬。保護成員 (protected) 供派生類訪問。數據成員和共享操作常提至基類。
- 基類和派生類的構造: 派生類對象包含基類子對象。構造函式呼叫順序:先基類,後派生類。派生類構造函式可通過成員初始化列表向基類構造函式傳參。派生類通常不能直接初始化基類資料成員。
-
析構函式: 呼叫順序:先派生類,後基類。通過基類指標刪除派生類對象時,基類析構函式必須是虛擬的 (
virtual ~Base()),否則可能僅呼叫基類析構函式,導致資源洩漏。 -
虛擬函式:
virtual關鍵字標記,實現動態綁定。通過基類指標/引用呼叫虛擬函式時,根據對象實際類型在運行時決定呼叫哪個版本。非虛擬函式靜態綁定(編譯時決定)。純虛擬函式 (= 0) 標記抽象函式,使類成為抽象基類。在構造/析構函式中呼叫虛擬函式總是靜態呼叫基類版本。派生類虛擬函式需與基類版本原型匹配(返回類型可例外)。虛擬函式的缺省實參靜態綁定。虛擬new操作符不存在,常使用clone()虛擬函式模擬。 - 按成員初始化與賦值: 繼承下,預設按成員操作會拷貝基類子對象和派生類部分成員。若基類或派生類包含指標或需要獨特語義,預設行為不足,需顯式拷貝構造函式/賦值操作符。顯式版本需負責基類子對對象和派生部分成員的正確拷貝。
- UserQuery 管理類: 引入類將查詢處理邏輯封裝,管理 Query 類層次對象的創建與銷毀。
第十八章 多繼承和虛擬繼承:更複雜的類關係
本章探討多繼承和虛擬繼承,解決單繼承無法建模的問題:
-
多繼承:
class Derived : access Base1, access Base2 { ... }派生類從多個基類繼承。派生類包含每個基類的子對象。構造順序依派生列表。析構順序相反。基類成員直接訪問可能因同名而二義。派生類指標/引用轉為基類指標/引用。虛擬函式在多繼承下行為。按成員初始化/賦值。 -
虛擬繼承:
class Derived : virtual access Base { ... }解決多繼承中同一基類多次出現導致子對象重複和二義性問題。虛擬基類在最終派生類中只有一個共享實例。初始化語義特殊:虛擬基類由最終派生類初始化。構造順序:先所有虛擬基類,後非虛擬基類,最後派生類。析構順序相反。虛擬基類成員的可見性。
第十九章 C++中繼承的用法:多態的應用與細節
本章探討多態的實際應用及其支持的語言特性:
-
RTTI (Run-Time Type Identification): 運行時識別多態對象的實際類型。
-
dynamic_cast:安全向下轉換基類指標/引用為派生類指標/引用。轉換失敗指標為nullptr,引用拋異常。 -
typeid:獲取對象的實際類型信息 (type_info對象)。type_info::name()返回類型名 C 風格字串。 - RTTI 僅用於帶虛擬函式的類。
-
-
異常和繼承: 將異常定義為類層次結構。
- 拋出派生類異常:可被基類
catch捕獲。 -
catch子句順序:派生類catch必須在基類catch之前。 - 異常對象與虛擬函式:基類
catch處理派生類異常時,需用引用才能呼叫派生類虛擬函式。 - 棧展開與析構函式:異常拋出時,棧展開會自動呼叫局部對象析構函式,實現 RAII (Resource Acquisition Is Initialization)。
- 異常規範:繼承下,派生類虛擬函式的異常規範不能比基類窄。
- 函數 try 塊:在構造函式中捕獲成員初始化列表拋出的異常。
- C++ 標準庫異常類層次:
exception為根,派生出logic_error和runtime_error等。
- 拋出派生類異常:可被基類
-
重載解析與繼承: 繼承影響函式重載解析過程。
- 候補函式:基類成員、基類名字空間函式、基類友元函式也會加入。
- 可行函式與使用者定義轉換序列:考慮繼承來的轉換函式/構造函式。派生類轉基類是標準轉換。
- 最佳可行函式:繼承來的轉換會影響轉換序列等級比較。
第二十章 iostream 庫:輸入輸出的抽象與應用
本章詳細介紹標準函式庫的 iostream 庫,用於輸入輸出:
-
iostream 概述:
iostream庫提供輸入輸出功能,基於多重繼承和虛擬繼承的類層次。包括標準流物件cin,cout,cerr。主要通過重載的>>(輸入) 和<<(輸出) 操作符進行操作。支持文件 I/O (fstream) 和字串流 (sstream)。支持char和wchar_t字元類型。 -
輸出操作符
<<: 基本用法,可接受多種類型參數(內建、字串、複數、指標),可串聯。介紹endl等操縱符 (manipulator)。指針輸出默認十六進制。可重載以支持使用者定義類型輸出。非成員操作符重載常用於對稱性。 -
輸入操作符
>>: 基本用法,可接受多種類型參數。可串聯。讀取時默認跳過空白字元。輸入錯誤會設置流的錯誤狀態。可重載以支持使用者定義類型輸入。 -
其他輸入操作:
get()讀取單個字元(包括空白),getline()讀取一行,read()讀取指定位元組數,gcount()返回實際讀取字元數。putback(),unget(),peek()。 -
檔案輸入輸出:
ifstream(輸入),ofstream(輸出),fstream(輸入輸出)。構造函式指定檔名和模式(如ios_base::in,out,app)。可使用open()和close()成員函式。可檢查文件是否成功打開。seekg()/seekp()重新定位文件位置,tellg()/tellp()獲取當前位置。 -
條件狀態: 流對象維護狀態標誌 (如
eofbit,badbit,failbit,goodbit)。eof(),bad(),fail(),good()查詢狀態。setstate()設置狀態,clear()清除狀態。rdstate()獲取狀態。 -
字串流:
istringstream從字串讀,ostringstream寫到字串,stringstream雙向。str()成員函式獲取底層字串。用於在內存中格式化或解析字串。 -
格式狀態: 流對象的格式狀態控制輸出細節(进制、精度、對齊)。通過操縱符修改狀態。
boolalpha,hex,oct,dec,showbase,showpoint,scientific,fixed,uppercase控制格式。setprecision(),setw(),setfill(),setbase()控制數值寬度和精度。 - 強類型庫: iostream 庫是強類型,編譯器檢查確保正確使用輸入輸出操作符。
comments
comments for this post are closed