首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 基礎與 STL 核心技術大全:從語言特性到 Effective C++ 實戰精華

     編輯:關於C++

C++ 語言核心特性深度解析

static 關鍵字的多重語義

static 在 C++ 中根據上下文呈現三種截然不同的行為:

  • 函數內靜態變量:儲存於靜態資料區,生命週期贯穿整個程式執行期間,初始化僅執行一次,多次呼叫保持值不變
  • 模組級別(.cpp 檔案):修飾全域變數或函數時,限制其連結可見性為內部連結,外部模組無法存取,實現封裝隔離
  • 類成員:屬於類本身而非具體物件,所有實例共享同一份儲存空間,透過類名或物件均可存取

全域變數 vs 全域靜態變數

單一原始檔專案中兩者無差異;多檔案專案中,static 全域變數僅在定義該變數的編譯單元內可見,有效避免符號衝突,是大型專案模組化的基礎技術。

動態記憶體管理:new/delete vs malloc/free

特性new/deletemalloc/free
本質C++ 運算子,可重載C 標準庫函數
建構/解構自動呼叫建構函式/解構函式僅分配/釋放原始記憶體
型別安全回傳帶型別指標回傳 void*,需強制轉型
失敗處理拋出 std::bad_alloc 異常回傳 NULL
實現關係底層通常呼叫 malloc/free系統級記憶體分配介面

關鍵區別:delete 僅呼叫一次解構函式,delete[] 會依序呼叫陣列每個元素的解構函式,混用會導致未定義行為。

引用:型別安全的別名機制

引用是變數的別名,編譯器層面實現,無額外記憶體開銷。核心規則:

  1. 宣告時必須初始化,且不可重新綁定
  2. 不存在空引用,比指標更安全
  3. 本質上透過指標實現,但對程式設計師透明
  4. 主要優勢:將 & 操作從呼叫端移至被呼叫端,簡化介面設計

結構體與類的細微差異

唯一語法差異:struct 預設存取層級為 public,class 為 private。繼承時同理。記憶體佈局、成員函式、虛函數機制完全一致。

const 完整應用指南

常量定義

const int MAX_SIZE = 100; // 必須初始化,具有型別檢查優勢

指標與 const 的四種組合

const int* p1;     // 指向常數的指標,內容不可修改
int const* p2;     // 同上,語法糖
int* const p3 = &x; // 常數指標,指向固定位址
const int* const p4 = &x; // 雙重約束

函數相關應用

  • 參數:void func(const int& param) 避免拷貝且保護資料
  • 回傳值:const int func() 防止修改暫時物件
  • 成員函數:void func() const 承諾不修改非靜態成員,const 物件只能呼叫 const 成員函數

const vs #define

const 擁有型別、作用域、命名空間約束,支援除錯符號;#define 純文字替換,無型別檢查,汙染全域命名空間。

陣列與指標的本質區別

  • 陣列名代表一塊固定大小的連續記憶體,地址與容量在生命週期內不變
  • 指標可指向任意記憶體,特徵是「可變」,適合動態記憶體操作
  • sizeof(array) 得到陣列總大小,sizeof(ptr) 得到指標本身大小(通常 4/8 位元組)
  • 陣列作為參數傳遞時退化為指標,失去大小資訊

隱式型別轉換三大類

  1. 算術轉換:向較寬型別提升防止精度損失,char/short 先提升為 int
  2. 賦值轉換:右值隱式轉為左值型別,可能發生截斷或捨入
  3. 輸出轉換:根據格式化規格自動轉換

sizeof vs strlen

特性sizeofstrlen
本質編譯期運算子執行期函數
計算對象型別、變數、陣列、指標以 null 結尾的字串
陣列參數編譯期分配的總位元組數字串實際長度(不含結尾 null)
指標參數指標變數大小(4/8 位元組)從該位址尋找 null 為止的長度

多態與虛表機制深度剖析

核心原理

  1. 含虛函數的類(非物件)擁有一個虛表,儲存虛函數位址
  2. 含虛函數的物件擁有虛表指標(vptr),指向其類的虛表
  3. 衍生類覆寫虛函數時,虛表對應項更新為新函數位址
  4. 透過基類指標呼叫虛函數:vptr → vtable → function address,實現動態綁定

虛表儲存位置

  • Linux/Unix:唯讀資料段
  • MSVC:常數段

靜態聯編 vs 動態聯編

  • 靜態:編譯期確定呼叫關係(非虛函數、樣板函數、內聯函數)
  • 動態:執行期根據物件實際型別決定(虛函數呼叫)

C++ 程式記憶體分佈圖解

高位址
├─────────────────┤ 棧區 - 自動分配釋放,向下增長
├─────────────────┤ 堆區 - 手動管理,向上增長
├─────────────────┤ 全域/靜態區 - 已初始化/未初始化分區
├─────────────────┤ 常數區 - 字串常值
├─────────────────┤ 程式碼區 - 二進位指令
低位址

棧向低位址增長,堆向高位址增長,中間留有巨大間隙防止碰撞。

變數初始化規則總結

  • 靜態儲存期(全域、static):編譯器自動零初始化,再執行使用者初始化
  • 自動儲存期(區域變數):無初始化則值不定
  • 動態分配:new Type() 值初始化,new Type 預設初始化(內建型別不初始化)

指標運算與類型大小

int* ptr;
ptr++; // 等價於 ptr + 1*sizeof(int),自動按元素大小步進
ptr2 - ptr1; // 計算元素個數差值,編譯器自動除以 sizeof(int)

內聯函數:空間換時間

  • 編譯期請求,編譯器可拒絕(遞迴、迴圈、過大函數)
  • 適用:簡短、高頻呼叫、無複雜控制流程
  • 優於巨集:有型別檢查、遵循作用域規則、可除錯

類物件大小計算

sizeof(Class) = 非靜態資料成員總和 + 對齊填充 + 虛函數指標(若有虛函數)

  • 空類大小為 1 位元組(確保不同物件有唯一位址)
  • 靜態成員、成員函數、虛表不計入物件大小
  • 虛繼承增加額外指標/偏移量開銷

指標進階類型辨析

宣告名稱含義
int (*f)(int)函數指標指向函數的指標變數
int* f(int)指標函數回傳指標的函數
int* a[10]指標陣列陣列元素皆為指標
int (*a)[10]陣列指標指向陣列的指標
int const* p常數指標指向常數,內容不可改
int* const p指標常數指標本身不可改

句柄 vs 指標

句柄是雙重間接索引:Handle → 表格索引 → 核心物件位址。Windows 核心物件(檔案、執行緒、視窗)頻繁移動/變更,句柄提供穩定介面,隱藏實作細節;指標直接持有記憶體位址,適用於單一進程內部。

RAII 與智能指標

auto_ptr(C++11 棄用,C++17 移除)

  • 獨占所有權,複製/賦值轉移所有權
  • 解構時自動 delete,不支援陣列

shared_ptr

  • 共享所有權,引用計數機制
  • 最後一個 shared_ptr 銷毀時自動釋放資源
  • 執行緒安全的控制塊,適合非循環資料結構

weak_ptr

打破循環引用,不增加引用計數,需 lock() 取得 shared_ptr 後使用。

this 指標特性

  • 不佔用物件大小,隱含傳遞給非靜態成員函數
  • 型別為 Class* const,不可修改指向
  • 建構函數執行前構造,解構函數結束後銷毀

C++ 編譯四階段

  1. 預處理:展開巨集、包含檔案、條件編譯
  2. 編譯:產生組合語言
  3. 組譯:產生目標機器碼
  4. 連結:解析符號引用,生成可執行檔

size_t 與可移植性

  • 無號整數型別別名,足以表示平台最大物件大小
  • 標準庫容器使用 size_type(通常為 size_t)
  • 避免使用 int 進行容器索引/大小運算
  • size_t(-1) 為最大值,邏輯判斷需謹慎

運算子重載規範

  • 本質為函數多載,單目運算子傾向成員函數,雙目運算子傾向友元函數
  • operator+ 回傳物件,operator= 回傳引用支援鏈式賦值
  • 流運算子 operator<< 回傳 ostream& 實現串聯
  • 參數求值順序:函數參數右至左,運算子運算元左至右

標頭檔包含最佳實踐

舊式現代 C++說明
<stdio.h><cstdio>C 標頭檔加 c 前綴,符號在 std 命名空間
<iostream.h><iostream>C++ 標頭檔移除 .h

繼承進階:多重與虛擬繼承

  • 多重繼承:單一類別繼承多個直接基類,需顯式指定同名成員
  • 虛繼承:解決菱形繼承重複基類問題,確保僅有一份基類子物件
  • 虛基類建構順序:最先建構,再按宣告順序建構衍生類
  • 虛繼承引入額外指標/偏移量開銷,非必要勿用

存取指定符可見性矩陣

類內/友元衍生類外部物件
private✓✗✗
protected✓✓✗
public✓✓✓

抽象類與純虛函數

class Abstract {
    virtual void interface() = 0; // 純虛函數
}; // 無法實例化,強制衍生類實作

C++ 型別轉換操作符

  • static_cast:編譯期檢查,基礎型別轉換、類別層級向上轉換
  • dynamic_cast:執行期 RTTI 檢查,安全向下轉型,需多態基類
  • const_cast:移除/添加 const 屬性
  • reinterpret_cast:位元級重新解釋,極度危險

命名空間最佳實踐

// 推薦:具體引入
using std::vector;
using std::string;

// 避免:汙染全域命名空間
using namespace std;

編譯器預設成員函數

  • 預設建構函數
  • 解構函數
  • 拷貝建構函數
  • 拷貝賦值運算子
  • 移動建構函數
  • 移動賦值運算子

淺拷貝僅複製成員位元,深拷貝需手動實作資源重新分配。

STL 標準模板庫核心組件

STL 架構三支柱

  1. 容器:資料結構實現
  2. 演算法:操作容器的通用函數
  3. 迭代器:統一存取介面

輔以配接器、配置器、函數物件等組件,實現泛型程式設計。

序列式容器對比

容器底層結構隨機存取頭尾插刪中間插刪適用場景
vector動態陣列O(1)尾部 O(1) 擴容O(n)預設首選,需隨機存取
deque分段連續陣列O(1)O(1)O(n)雙端高頻操作
list雙向鏈表不支援O(1)O(1)大量中間插刪

關聯式容器

容器底層鍵值有序重複鍵查找/插入/刪除
map紅黑樹key-value是否O(log n)
set紅黑樹key only是否O(log n)
multimap紅黑樹key-value是是O(log n)
multiset紅黑樹key only是是O(log n)
unordered_map雜湊表key-value否否平均 O(1)
unordered_set雜湊表key only否否平均 O(1)

容器配接器

  • stack:LIFO,預設基於 deque
  • queue:FIFO,預設基於 deque
  • priority_queue:堆積,預設大根堆,基於 vector

string 核心操作

s.size()/length()  // 字元數
s.empty()          // 是否為空
s[n] / s.at(n)     // 存取(at 有邊界檢查)
s.substr(pos, len) // 子字串
s.find(str)        // 查找,失敗回傳 npos
s.append(args)     // 尾部追加
reverse(s.begin(), s.end()) // 反轉

常用演算法速查

find(first, last, val)           // 線性查找
sort(first, last)                // 快速排序 O(n log n)
stable_sort(first, last)         // 穩定排序
reverse(first, last)             // 反轉序列
swap(a, b)                       // 交換兩物件
lower_bound/upper_bound          // 二分查找邊界

Effective C++ 55 條款精華總結

基礎觀念(條款 1-3)

  1. 視 C++ 為語言聯邦:C、OOP、Templates、STL 四大子語言
  2. 以 const/enum/inline 替代 #define
  3. 盡可能使用 const:語義明確、編譯器優化、介面契約

建構/解構/賦值(條款 4-12)

  1. 確保物件使用前已初始化:成員初始化列表、單例模式
  2. 了解編譯器隱式生成的函數
  3. 拒絕不需要的編譯器生成函數:private 未定義或繼承 Uncopyable
  4. 多態基類宣告 virtual 解構函數
  5. 解構函數勿拋出異常:吞下或終止
  6. 建構/解構期間不呼叫虛函數:虛機制尚未就緒/已拆除
  7. operator= 回傳 *this 參考支援鏈式賦值
  8. operator= 處理自我賦值
  9. 拷貝物件時勿漏任何成員:本地成員 + 基類部分

資源管理(條款 13-17)

  1. 以物件管理資源:RAII、shared_ptr/unique_ptr
  2. 資源管理類小心複製行為:禁止/引用計數/深拷貝
  3. 提供存取原始資源介面:隱式轉換或 get()
  4. new/delete 形式配對:陣列用 []
  5. 獨立語句將 new 物件置入智能指標:防異常洩漏

介面設計(條款 18-25)

  1. 介面易用難誤:新型別、限制轉換、一致性
  2. 設計 class 如設計 type:完整語義、效率、異常安全
  3. 傳參優先 const T& 替代 T:避免拷貝、防切割
  4. 必須回傳物件時勿回傳參考:區域/堆/靜態皆有陷阱
  5. 資料成員 private:封裝彈性、不變式維護
  6. 優先 non-member non-friend 替代成員函數:增強封裝
  7. 所有參數需型別轉換時用非成員函數:支援隱式轉換對稱性
  8. 考慮無拋出 swap:高效交換、強異常安全基石

實作細節(條款 26-31)

  1. 延後變數定義:降低建構成本、縮小作用域
  2. 盡量少轉型:隱藏設計缺陷、破壞型別系統
  3. 避免回傳內部成員控制權:維護封裝邊界
  4. 追求異常安全:基本/強烈/nothrow 三級保證
  5. 透徹理解 inline:編譯器建議、適合小熱點函數
  6. 降低編譯依賴:Pimpl 慣用語、介面類別

繼承與多態(條款 32-40)

  1. public 繼承模型 is-a:基類契約對衍生類生效
  2. 避免遮蔽繼承名稱:using Base::func
  3. 區分介面繼承與實作繼承:pure virtual vs impure virtual vs non-virtual
  4. 考慮 virtual 替代方案:NVI、策略模式、std::function
  5. 絕不重新定義繼承的 non-virtual 函數:靜態綁定導致行為不一致
  6. 絕不重新定義繼承的預設參數值:靜態綁定
  7. 組合模型 has-a / is-implemented-in-terms-of
  8. 謹慎使用 private 繼承:實作繼承、空白基類優化
  9. 謹慎使用多重繼承:虛繼承成本、介面分離原則

樣板與泛型程式設計(條款 41-48)

  1. 隱式介面與編譯期多態:樣板參數的操作需求
  2. typename 雙重語義:樣板參數聲明、嵌套從屬型別標識
  3. 處理樣板化基類名稱:this->、using
  4. 抽離與參數無關代碼:避免程式碼膨脹
  5. 成員函數樣板接受相容型別:智慧指標轉換
  6. 需型別轉換時定義非成員函數:支援隱式轉換
  7. 使用 traits classes 表達型別特性:標準化型別資訊查詢
  8. 認識樣板元程式設計:編譯期計算、型別運算

自訂記憶體管理(條款 49-52)

  1. 理解 new_handler 機制:分配失敗回調
  2. 替換 new/delete 時機:檢測錯誤、效能優化、統計收集
  3. 遵循慣例:迴圈分配、大小檢查、空指標處理
  4. 提供 placement new 需對應 placement delete

綜合建議(條款 53-55)

  1. 重視編譯器警告:最高級別、視同錯誤
  2. 熟悉標準程式庫含 TR1/C++11:std::array, tuple, regex, thread 等
  3. 熟悉 Boost:標準庫孵化器、跨平台高品質庫
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved