C++ 建構函式中檢查記憶體配置失敗,assert(ptr != nullptr) 能否替換為 if (ptr == nullptr) { exit(1); }?本文從編譯期、執行期、發佈流程三個維度深度對比兩者本質差異。
| 維度 | assert(expr) | if (!expr) exit(1) |
|---|---|---|
| 編譯期行為 | Debug 保留、Release 完全移除(宏展開為空) | 所有建置模式均保留 |
| 執行期成本 | Debug 有分支檢查、Release 零成本 | 所有模式均有分支檢查成本 |
| 用途定位 | 開發期斷言:檢查「不可能發生」的程式邏輯錯誤(前置/後置條件、不變式) | 生產期錯誤處理:處理「可能發生」的運行時錯誤(資源耗盡、輸入無效、網路失敗) |
| 偵錯資訊 | 自動包含檔案、行號、表達式文本、呼叫堆疊 | 需自行輸出日誌 |
| 可攜性 | 標準 C/C++ (<cassert>) | 標準 C/C++ |
// 方式一:assert(僅 Debug 生效)
SeqStack(int sz) : top(-1), maxsize(sz) {
elements = new T[maxsize];
assert(elements != nullptr); // Release 直接移除,若 new 失敗拋 bad_alloc 則不會觸發
}
// 方式二:if + exit(所有模式生效)
SeqStack(int sz) : top(-1), maxsize(sz) {
elements = new (std::nothrow) T[maxsize]; // 不拋異常,失敗回傳 nullptr
if (elements == nullptr) {
std::cerr << "記憶體配置失敗" << std::endl;
std::exit(EXIT_FAILURE);
}
}
// 方式三:現代 C++ 智慧指標 + 異常(推薦)
SeqStack(int sz) : top(-1), maxsize(sz), elements(std::make_unique(maxsize)) {
// new 失敗自動拋 std::bad_alloc,由上層捕獲或終止程式
}
「assert 在 Release 被移除,所以生產環境不會檢查,不安全!」
這正是設計初衷:assert 斷言的是「程式邏輯必然為真」的條件。若配置失敗屬於「可能發生的運行時錯誤」,不應用 assert,而應用錯誤處理機制(異常、錯誤碼、Result 型別)。
assert(如:索引範圍、指標非空、狀態機合法轉移)。new (nothrow))。NDEBUG 宏(-DNDEBUG 或 /DNDEBUG)啟用 Release 模式移除 assert。ENSURE(expr) 宏,Release 保留並記錄日誌而非終止。assert 是開發期檢查工具,if+exit 是生產期錯誤處理。兩者用途正交,不可互換。現代 C++ 建議:資源管理用 RAII/智慧指標,錯誤傳播用異常,契約檢查用 assert(或 C++20 <contract> 提案),各司其職,構建健壯軟體。