重载new操作符时出现异常段错误的问题排查
问题根源与解决方案
首先,咱们得明确你遇到的段错误核心原因:你重载的抛出式operator new违反了C++标准约定,在分配失败时返回了nullptr,而带参数构造/值初始化的new表达式会直接在这个空指针上执行内存写入操作,导致崩溃。
为什么不同场景表现不同?
咱们拆解一下各个场景的行为:
new Object()(无参数构造):你的Object默认构造函数是空的,没有任何内存写入操作——即使operator new返回nullptr,调用空构造函数也不会触发内存访问错误,所以看起来“正常”。new double(默认初始化):对于内置类型double,默认初始化不会修改内存内容——空指针指向的内存没被写入,所以也不会崩溃。new Object(5)或new double()(带参构造/值初始化):这两种情况都会执行内存写入操作:- 带参构造函数会给
sum赋值,本质是往nullptr指向的内存写数据; new double()是值初始化,会把double置为0,同样要往空指针地址写数据。
这两种操作都会直接触发段错误,因为你试图访问并修改无效的内存地址。
- 带参构造函数会给
另外,C++标准明确规定:不带nothrow参数的operator new(也就是你重载的这个版本)是抛出式分配函数,它必须成功分配内存,否则应该抛出std::bad_alloc异常,而不是返回nullptr。编译器生成的代码会默认这个版本的operator new永远返回有效指针,所以不会做空指针检查,直接执行后续的构造/初始化操作。
解决方案
根据你的需求,有两种正确的实现方式:
1. 遵循标准,实现抛出式operator new
修改你的operator new,在分配失败时抛出std::bad_alloc异常,而不是返回nullptr:
#include <stdexcept> // 引入std::bad_alloc void* operator new(std::size_t size) { void* chunk = mm.getAChunk(size); if (chunk == nullptr) { throw std::bad_alloc(); // 抛出异常,代替返回nullptr } return chunk; }
这样,当分配失败时,程序会抛出异常,你可以在代码中用try-catch块捕获并处理,而不会触发段错误。
2. 实现不抛出式operator new(支持返回nullptr)
如果你希望保留“分配失败返回nullptr”的行为,需要重载不抛出版本的operator new,并使用对应的new表达式语法:
#include <new> // 引入std::nothrow_t和std::nothrow // 重载不抛出版本的operator new void* operator new(std::size_t size, const std::nothrow_t&) noexcept { void* chunk = mm.getAChunk(size); // 分配失败直接返回nullptr,符合该版本的约定 return chunk; } // 对应的使用方式 int main() { // 使用std::nothrow来调用不抛出版本的new Object* obj1 = new(std::nothrow) Object(5); if (obj1 == nullptr) { cout << "- Could not allocate space -" << endl; } double* d1 = new(std::nothrow) double(); if (d1 == nullptr) { cout << "- Could not allocate space -" << endl; } }
这种方式下,编译器会生成空指针检查的代码,你可以在代码中判断返回值是否为nullptr,避免后续的非法内存访问。
额外注意事项
- 记得同时重载对应的
operator delete,确保内存池的内存能正确回收,避免泄漏; - 检查你的
getAChunk方法,确保分配失败时不会破坏内存池的currentAddress等状态; - 明确区分C++中不同的初始化行为,避免因内置类型的初始化规则导致意外错误。
内容的提问来源于stack exchange,提问作者Dani Gilboa
相关产品推荐
相关产品推荐

