本文記錄了《0 Bug - C/C++ 商用工程之道》作者肖舸 (網名 TonyXiao) 與資深 C++ 專家陳碩 (muduo 作者、前騰訊高級工程師) 圍繞該書展開的深度技術討論。雙方分別代表**「工程落地派」(肖舸)** 與 **「現代 C++ 規範派」(陳碩)**** 的觀點交鋒,涵蓋 `goto`、宏、自定義鎖、內存池、異常處理、拷貝構造、跨平台適配等核心議題,極具參考價值。
陳碩 (反對派):C++ 中 `goto` 無法跨越變量初始化,編譯器報錯;結構化編程 (`break`/`continue`/`return`/RAII) 足以替代,`goto` 破壞控制流可讀性。
肖舸 (支持派):多任務/並行環境下,資源獲取與釋放呈現嚴格的「二元動作嵌套」 (`lock`/`unlock`, `malloc`/`free`, `new`/`delete`)。深層嵌套時,結構化跳轉 (`break`/`continue`) 無法精確指定落點,**`goto` 能實現精確的層級跳出**,簡化錯誤處理路徑代碼。
共識:`goto` 不是不能用,但**嚴禁濫用**,僅限於「錯誤處理統一出口」等局部受控場景,且需團隊規範約束。
陳碩:不喜歡宏,破壞類型安全、調試困難、作用域污染。
肖舸:宏的**編譯期計算固化**、**無類型檢查 (視為優點)**、**運行期結果確定** 特性,符合函數式編程「無副作用、值不可變」思想。嘗試用宏構建「C 語言函數式風格」,利用其天然線程安全特性簡化並發編程。
現代視角:C++17 `constexpr`、C++20 `consteval`、模板元編程、反射提案,已在語言層面提供類型安全的編譯期計算能力,**新代碼應優先使用語言特性而非宏**。
陳碩 (核心反對):
肖舸 (工程妥協):
結論:**非極端約束 (舊平台、特殊內存模型) 下,嚴禁自造同步原語**。現代項目應基於 C++11/17/20 標準庫或成熟庫構建抽象層。
針對肖舸「內存池不支持 `new`/`delete`」痛點,陳碩給出標準現代 C++ 方案:
// 1. 內存池接口 (對齊分配)
class MemoryPool {
public:
void* alloc(size_t n); // 返回充分對齊地址
void dealloc(void* p);
};
// 2. Placement New 手動構造/析構
void* mem = pool.alloc(sizeof(BigInt));
BigInt* p = new (mem) BigInt(); // 定位構造
// 使用 p...
p->~BigInt(); // 手動析構
pool.dealloc(p);
// 3. 封裝為 Factory 模板 (零開銷抽象)
template
class PoolFactory {
MemoryPool* pool_;
public:
explicit PoolFactory(MemoryPool* p) : pool_(p) {}
T* create() {
void* mem = pool_->alloc(sizeof(T));
return new (mem) T();
}
void destroy(T* p) {
p->~T();
pool_->dealloc(p);
}
};
// 使用
BigInt* p = PoolFactory(&pool).create();
PoolFactory(&pool).destroy(p); // 臨時對象優化掉
此方案**類型安全、支持構造函數參數、RAII 友好、編譯期內聯零開銷**,完美解決「內存池 + 對象語義」矛盾。
陳碩:包含 `mutex` 的類 (`CMInt`, `CMBool`, `CMutexLock`) **必須刪除拷貝構造/賦值** (`= delete`),否則雙重銷毀、鎖共享導致未定義行為。忽視 Rule of Three/Five 是不合格 C++ 代碼。
肖舸:團隊鐵律**「禁止對象賦值/傳值,只傳指針/引用」**,從規範層面杜絕拷貝場景,故不實現拷貝操作。類比「線程 ID 只能用於 kill,不保存」的防錯誤哲學。
現代最佳實踐:
陳碩:**對象不能用自身的鎖保護自身的銷毀**。析構函數中加鎖解鎖無意義,因為調用析構時對象已進入「死亡狀態」,其他線程持有的指針/引用已成懸空。正確方案:
肖舸** 承認析構加鎖非正規解法,但作為「防呆」手段:若程序員未正確 `Stop()` 導致殘留線程訪問,析構加鎖可延緩崩潰、賺取日志機會。
雙方共識:反對濫用 C++ 異常,理由高度一致:
差異:陳碩認為 Java `try-catch-finally` 機制較優 (finally 保證解鎖);肖舸堅持異常本質是破壞確定性控制流。
工程建議:**庫/框架/系統級代碼禁用異常,應用層業務邏輯可謹慎使用 (配合 RAII),統一在邊界轉為錯誤碼/Result 類型**。
肖舸分享成熟工作流:
這場對話折射出 C++ 開發的核心張力:**語言理想 (現代 C++、RAII、標準庫、類型安全) vs 現實約束 (舊平台、嵌入式資源受限、團隊水平參差、歷史代碼包袱、7×24 高可用)。**
**給讀者的啟示**:
這場跨越十年的對話,至今仍是 C++ 工程師必讀的「避坑指南」與「思維升級」教材。