new失败时抛std::bad_alloc而非返回nullptr,因C++默认使用异常版本new;未捕获会导致程序abort,STL容器操作同样可能触发,需在分配路径全程try-catch并妥善降级。

new 失败时为什么抛 std::bad_alloc 而不是返回 nullptr
因为 C++ 默认使用的是「抛异常版本」的 new,它在分配失败时不会静默返回 nullptr,而是直接抛出 std::bad_alloc。这和 C 的 malloc 行为完全不同,也容易让刚转 C++ 的人措手不及——没写 try 就崩溃了。
常见错误现象:terminate called after throwing an instance of 'std::bad_alloc',程序直接 abort,连堆栈都来不及打印。
- 除非显式调用
new(std::nothrow),否则所有裸new都是异常语义 -
std::vector::reserve()、std::string::resize()等内部调用 new 的操作,同样可能抛std::bad_alloc - 即使你没写
new,STL 容器扩容也可能触发它——别以为只有自己手动 new 才要管
捕获 std::bad_alloc 的正确姿势
不能只在 new 附近 try-catch,得覆盖整个可能触发分配的路径。比如构造一个含 vector 成员的类对象,构造函数里 vector 自动分配内存,异常就发生在初始化列表阶段。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用
try { ... } catch (const std::bad_alloc& e) { ... },不要捕获非 const 引用或值(避免切片) - catch 块里别再 new —— 你已经处于内存紧张状态,二次分配大概率再崩
- 优先做清理、日志、降级(如切到预分配缓冲区),而不是“重试”
- 如果是在库函数或回调里,确保异常能透出或转成错误码(取决于接口契约)
示例:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
try {
std::vector<int> v(1024 * 1024 * 1024); // 请求 4GB
} catch (const std::bad_alloc& e) {
std::cerr << "OOM: " << e.what() << "\n";
fallback_to_disk_buffer(); // 切到文件临时缓存
}用 new(std::nothrow) 替代异常?什么场景适合
适合对异常敏感、无法/不愿引入异常处理机制的环境,比如嵌入式、实时系统、C 接口封装层。
但要注意:
-
new(std::nothrow)返回nullptr,必须每处都判空——漏一个就 UB - 它只影响本次 new,不改变后续 STL 分配行为(
std::vector内部仍用 throw 版本) - 编译器可能优化掉部分判空逻辑(尤其 Release 模式),静态分析工具比运行时更依赖人工检查
- 和 RAII 不天然兼容:智能指针构造时仍会 throw,不能靠 nothrow new 绕过
典型用法:
int* p = new(std::nothrow) int[1000000];
if (!p) {
log_error("allocation failed");
return -1; // 或其他错误传播方式
}真正防住 OOM:从设计上减少动态分配依赖
处理 std::bad_alloc 是事后补救,真正健壮的系统得提前压分配量。很多看似“必须 new”的地方其实有替代方案。
- 用栈数组或
std::array替代小规模new[](比如长度 ≤ 1024) - 预分配池:对频繁创建销毁的小对象,用
boost::pool或自定义 freelist - 限制容器增长:
vector::reserve()配合容量检查,避免隐式翻倍扩容 - 改用 arena/slab 分配器(如
absl::InlinedVector、llvm::SmallVector)——它们优先用栈空间,超限时才 fall back 到 heap
最容易被忽略的一点:std::string 和 std::vector 的 move 构造函数不抛异常,但 copy 构造可能触发分配。传参时多用 const std::string& 或 std::string&&,少无脑 copy。

















