跳转到内容

C++ new 和delete 重载

来自代码酷

C++运算符重载:new和delete重载[编辑 | 编辑源代码]

介绍[编辑 | 编辑源代码]

在C++中,newdelete运算符用于动态内存管理。与其他运算符一样,它们也可以被重载,允许程序员自定义内存分配和释放的行为。重载这些运算符在需要精细控制内存管理时特别有用,例如在嵌入式系统、高性能计算或实现自定义内存池时。

基本语法[编辑 | 编辑源代码]

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++提供的一个强大特性,允许程序员完全控制内存管理。通过合理使用这一特性,可以实现:

  • 自定义内存分配策略
  • 内存使用跟踪和调试
  • 特定领域的内存优化
  • 资源管理扩展

对于大多数应用程序,标准的内存管理已经足够,但在需要特殊内存处理的情况下,重载这些运算符提供了极大的灵活性。