繼上篇之後,本文繼續深入剖析 C++/VC++ 開發中常見的棘手問題:std::list 迭代器隨機移動、vector::resize vs reserve 差異、vector 內存重分配導致指針失效、靜態庫與動態庫建立調用完整流程。
問題: list 內部為雙向鏈表,迭代器僅支援 ++/--,不支援 +n、-n、+=、-=。
錯誤做法: 手寫 for(int i=0;i
標準解法:std::advance
#include#include
// advance 所在 std::list myList; // ... 初始化 ... std::list ::const_iterator it = myList.begin(); std::advance(it, 10); // 向前移動 10 位置,負數向後 // advance 內部針對隨機存取迭代器 (vector/deque) 優化為 O(1), // 雙向迭代器 (list/map/set) 則迴圈 ++,完全封裝細節。
建議: 永遠使用 std::advance、std::distance、std::next/std::prev (C++11) 處理迭代器位移,容器無關、意圖清晰。
| 特性 | reserve(n) | resize(n, val) |
|---|---|---|
| 用途 | 預留容量,避免重新分配 | 改變實際大小,構造元素 |
| 元素構造 | 不構造 元素 | 構造 n 個元素 (預設或指定值) |
| 可存取性 | 不可用 [] 或迭代器存取 | 可直接用 []/迭代器讀寫 |
| 新增元素 | 必須 push_back/insert | 直接賦值 vec[i]=val |
| 參數 | 單參數 (容量) | 雙參數 (大小, 值) 第二個可省略 |
std::vectorv; v.reserve(100); // 容量 100,size=0 for(int i=0;i<100;i++) v.push_back(i); // 逐個構造 v.resize(102); // size=102,新增 2 個 0 v[100] = 1; v[101] = 2; // 直接操作
場景: 結構體內含指向同 vector 內其他元素的指針。
struct Node { std::string name; Node* prev; };
std::vector vec;
for(...) {
Node n; n.prev = &vec[some_index]; // 危險!
vec.push_back(n); // 可能觸發擴容,導致所有 prev 指針失效 (懸空指針)
}
解決方案:
vec.reserve(estimated_max) 避免重新分配。size_t prev_index,存取時 vec[prev_index],索引不隨移動失效。std::list: 節點記憶體地址穩定,指針/引用永不失效 (但失去隨機存取)。extern "C" RetType FuncName(Params); (防止 C++ 名稱修飾)。.lib + .h。.h,專案屬性 Linker -> Input -> Additional Dependencies 加入 mylib.lib。直接調用函數。extern "C" __declspec(dllexport) RetType FuncName(Params);。.dll、.lib (導入庫)、.h。mydll.lib。extern "C" __declspec(dllimport) RetType FuncName(Params);。.dll 需在 PATH 或同目錄。| 項目 | 靜態庫 | 動態庫 |
|---|---|---|
| 代碼複製 | 連結時複製進 EXE | 運行時動態載入 |
| 體積影響 | 增大 EXE | EXE 較小 |
| 更新部署 | 需重新編譯連結 | 僅替換 DLL (介面兼容前提) |
| 巢狀依賴 | 不能 包含其他靜態/動態庫 | 可以 內部鏈接其他庫 |
| 調試便利性 | 方便 (代碼在 EXE 內) | 較麻煩 (需載入符號) |
| 螢幕輸出 | 可用 cout | 不可 用 cout (無控制台) |
掌握這些核心概念,能讓你在 C++ 基礎設施層面遊刃有餘。