条款51:编写 new 和 delete 时需固守常规
在编写自定义的 new
和 delete
操作符时,需要遵循一些常规和良好的实践。这样做不仅有助于确保内存管理的正确性,还可以提高程序的健壮性和性能。
使用场景
-
operator new
:
operator new
应该包含一个无穷循环,在尝试分配内存时,如果无法满足内存需求,它应该调用new-handler
。另外,它应能够处理 0 字节的申请,并处理错误申请(例如请求的内存大小超过了正确大小)。 -
operator delete
:
operator delete
应该在接收到空指针时不进行任何操作。对于类专属的版本,它还应该处理比正确大小更大的错误申请,确保内存的正确释放。
示例:自定义 operator new
和 operator delete
void* Base::operator new(std::size_t size) throw(std::bad_alloc) { if (size != sizeof(Base)) { return ::operator new(size); // 正常的内存分配 } // 在此处可以处理专门为 Base 类分配的内存 return ::operator new(size); // 假设是针对 Base 类的内存分配 } void Base::operator delete(void* rawMemory, std::size_t size) throw() { if (rawMemory == nullptr) return; // 空指针不处理 if (size != sizeof(Base)) { ::operator delete(rawMemory); // 错误大小时的处理 return; } // 正常的内存释放 }
在这个示例中,operator new
和 operator delete
都进行了额外的检查和处理,确保内存分配和释放的正确性。如果请求的内存大小不符,或传递的是空指针时,程序会做出适当的响应。
注意事项
-
处理 0 字节申请:
operator new
应该能够处理 0 字节的内存申请,尽管分配 0 字节通常并不常见,但处理这种特殊情况能增强代码的健壮性。 -
内存大小检查:
当自定义delete
时,应该检查是否请求了与实际对象大小不同的内存。如果内存大小不符,通常应调用系统的默认delete
。 -
内存泄漏预防:
为了避免内存泄漏和未定义的行为,必须确保delete
操作能正确处理任何类型的内存释放。特别是当内存被错误地分配或大小不一致时,要保证合适的清理。 -
不对空指针进行操作:
operator delete
在接收到空指针时应该直接返回,不做任何操作。这是因为删除一个空指针是合法的,并且不会导致内存错误。
总结
编写自定义的 new
和 delete
操作符时,遵循一些常规的做法是非常重要的。通过合理处理内存分配和释放时的特殊情况,可以避免内存泄漏、提高代码的健壮性并提升程序性能。特别是在处理大小不一致的内存、空指针和 0 字节申请时,细致的检查和相应的操作是必不可少的。