C++ new 和delete 重载
外观
C++运算符重载:new和delete重载[编辑 | 编辑源代码]
介绍[编辑 | 编辑源代码]
在C++中,new和delete运算符用于动态内存管理。与其他运算符一样,它们也可以被重载,允许程序员自定义内存分配和释放的行为。重载这些运算符在需要精细控制内存管理时特别有用,例如在嵌入式系统、高性能计算或实现自定义内存池时。
基本语法[编辑 | 编辑源代码]
new和delete运算符可以以成员函数或全局函数的形式重载。以下是基本语法:
// 全局重载
void* operator new(size_t size);
void operator delete(void* ptr) noexcept;
// 类成员重载
class MyClass {
public:
void* operator new(size_t size);
void operator delete(void* ptr) noexcept;
};
重载new运算符[编辑 | 编辑源代码]
重载new运算符时,必须返回void*,并接受一个size_t参数(表示要分配的字节数)。下面是一个简单的全局new重载示例:
#include <iostream>
#include <cstdlib>
void* operator new(size_t size) {
std::cout << "自定义new分配了 " << size << " 字节\n";
void* ptr = malloc(size);
if (!ptr) throw std::bad_alloc();
return ptr;
}
int main() {
int* p = new int(42);
std::cout << "*p = " << *p << "\n";
delete p;
return 0;
}
输出:
自定义new分配了 4 字节 *p = 42
重载delete运算符[编辑 | 编辑源代码]
delete运算符重载必须返回void,接受一个void*参数(指向要释放的内存),并且应该标记为noexcept:
void operator delete(void* ptr) noexcept {
std::cout << "自定义delete释放内存\n";
free(ptr);
}
数组版本[编辑 | 编辑源代码]
也可以重载new[]和delete[]:
void* operator new[](size_t size) {
std::cout << "自定义new[]分配了 " << size << " 字节\n";
void* ptr = malloc(size);
if (!ptr) throw std::::bad_alloc();
return ptr;
}
void operator delete[](void* ptr) noexcept {
std::cout << "自定义delete[]释放内存\n";
free(ptr);
}
类特定重载[编辑 | 编辑源代码]
在类内部重载new和delete时,这些运算符仅适用于该类的对象:
class MemoryTracker {
public:
void* operator new(size_t size) {
std::cout << "为MemoryTracker分配 " << size << " 字节\n";
return ::operator new(size); // 使用全局new
}
void operator delete(void* ptr) noexcept {
std::cout << "释放MemoryTracker内存\n";
::operator delete(ptr); // 使用全局delete
}
};
放置new[编辑 | 编辑源代码]
除了常规new,还可以重载放置new(placement new),它允许在已分配的内存上构造对象:
#include <new>
void* operator new(size_t size, void* ptr) noexcept {
return ptr; // 只是返回传入的指针
}
// 使用示例
int main() {
char buffer[sizeof(int)];
int* p = new (buffer) int(42); // 在buffer上构造int
std::cout << *p << "\n";
p->~int(); // 显式调用析构函数
return 0;
}
实际应用案例[编辑 | 编辑源代码]
内存池实现[编辑 | 编辑源代码]
重载new和delete常用于实现内存池,提高频繁分配/释放小对象时的性能:
class MemoryPool {
struct Block { Block* next; };
Block* freeList = nullptr;
public:
void* allocate(size_t size) {
if (!freeList) {
// 分配大块内存并分割
freeList = static_cast<Block*>(malloc(1024));
for (int i = 0; i < 1024/sizeof(Block); ++i) {
freeList[i].next = &freeList[i+1];
}
freeList[1024/sizeof(Block)-1].next = nullptr;
}
Block* block = freeList;
freeList = freeList->next;
return block;
}
void deallocate(void* ptr) noexcept {
Block* block = static_cast<Block*>(ptr);
block->next = freeList;
freeList = block;
}
};
class PooledObject {
static MemoryPool pool;
public:
void* operator new(size_t size) { return pool.allocate(size); }
void operator delete(void* ptr) noexcept { pool.deallocate(ptr); }
};
内存泄漏检测[编辑 | 编辑源代码]
通过重载new和delete可以实现简单的内存泄漏检测:
#include <iostream>
#include <map>
std::map<void*, size_t> allocations;
void* operator new(size_t size) {
void* ptr = malloc(size);
allocations[ptr] = size;
std::cout << "分配: " << ptr << " (" << size << " 字节)\n";
return ptr;
}
void operator delete(void* ptr) noexcept {
auto it = allocations.find(ptr);
if (it != allocations.end()) {
std::cout << "释放: " << ptr << " (" << it->second << " 字节)\n";
allocations.erase(it);
}
free(ptr);
}
void checkLeaks() {
if (!allocations.empty()) {
std::cerr << "内存泄漏检测到 " << allocations.size() << " 个未释放块:\n";
for (const auto& [ptr, size] : allocations) {
std::cerr << " " << ptr << ": " << size << " 字节\n";
}
}
}
注意事项[编辑 | 编辑源代码]
- 重载new和delete会影响整个程序的内存管理,需谨慎使用
- 确保重载的delete标记为noexcept
- 重载new应该正确处理内存不足情况(抛出bad_alloc)
- 类特定重载优先于全局重载
- 重载placement new时需要提供对应的placement delete
性能考虑[编辑 | 编辑源代码]
重载内存管理运算符可能带来性能提升,但也可能引入开销。考虑以下因素:
- 自定义内存池可以减少系统调用
- 额外的簿记工作会增加内存使用
- 线程安全性需要考虑
总结[编辑 | 编辑源代码]
重载new和delete运算符是C++提供的一个强大特性,允许程序员完全控制内存管理。通过合理使用这一特性,可以实现:
- 自定义内存分配策略
- 内存使用跟踪和调试
- 特定领域的内存优化
- 资源管理扩展
对于大多数应用程序,标准的内存管理已经足够,但在需要特殊内存处理的情况下,重载这些运算符提供了极大的灵活性。