目录
二、operator new 和 operator delete 的基本原理
三、为什么要重载 operator new 和 operator delete
四、如何重载 operator new 和 operator delete
(二)类专属的 operator new 和 operator delete 重载
(三)全局 operator new 和 operator delete 重载
五、重载 operator new 和 operator delete 的注意事项
一、C++ 内存管理初印象
在 C++ 编程的世界里,内存管理堪称基石般的存在,其重要性怎么强调都不为过。内存管理的优劣,直接关乎程序的性能表现、稳定性以及资源利用率。一旦内存管理出现问题,比如内存泄漏,随着程序运行时间的增加,内存不断被消耗却得不到释放,最终会导致系统内存耗尽,程序崩溃;再比如野指针的产生,对其进行操作可能会导致程序访问非法内存地址,引发未定义行为,让程序出现各种难以调试的诡异问题。
C++ 中,动态内存的分配和释放主要依靠new和delete操作符 。当我们使用new创建一个对象时,它会在堆上为该对象分配内存,并调用对象的构造函数进行初始化。例如:
int* num = new int(5);
这行代码在堆上分配了一个int类型的内存空间,并将其初始化为 5 ,num指针指向这块内存。
而delete的作用则是释放由new分配的内存,并调用对象的析构函数。像这样:
delete num;
这就释放了num指向的内存空间,防止内存泄漏。
那operator new和operator delete函数又是什么呢?其实,new和delete操作符背后调用的就是operator new和operator delete函数 。operator new负责分配内存,当new操作符被使用时,它首先调用operator new函数来获取所需的内存空间,然后在这块内存上调用对象的构造函数;operator delete则负责释放内存,delete操作符会先调用对象的析构函数,然后调用operator delete函数来释放内存。简单来说,operator new和operator delete是内存分配和释放的底层实现函数,而new和delete操作符则为我们提供了更高级、更方便的语法糖,让我们能更简洁地进行内存管理操作。
二、operator new 和 operator delete 的基本原理
(一)operator new 的工作机制
operator new的主要职责是在堆上分配指定大小的内存空间 。它的实现其实是基于 C 语言的malloc函数。当我们调用operator new时,它会尝试通过malloc来申请内存 。比如下面这段代码:
void* operator new(size_t size) {
void* p = malloc(size);
if (!p) {
// 处理内存分配失败的情况
}
return p;
}
在这个函数中,malloc尝试分配size大小的内存空间 。如果malloc成功分配内存,operator new就直接返回指向这块内存的指针;但如果malloc失败,也就是返回了NULL指针,operator new就会着手处理内存分配失败的情况 。
通常,operator new在内存分配失败时,会尝试执行一些空间不足的应对措施 。比如,它可能会调用用户设置的new_handler函数 。这个new_handler函数是用户自定义的,用于处理内存不足的情况。它可以尝试释放一些其他的内存,以便为当前的内存分配请求腾出空间;或者它也可以抛出异常,表明内存分配失败 。如果用户没有设置new_handler函数,operator new就会抛出std::bad_alloc异常 ,这是 C++ 标准库定义的用于表示内存分配失败的异常类型 。
(二)operator delete 的工作机制
operator delete的作用是释放由operator new分配的内存空间 。它的工作过程和free函数紧密相关 。operator delete的基本实现如下:
void operator delete(void* p) {
if (p) {
free(p);
}
}
从这段代码可以看出,当operator delete接收到一个非空指针p时,它会调用free函数来释放p指向的内存空间 。free函数是 C 语言标准库中的函数,专门用于释放由malloc、calloc或realloc分配的内存 。在operator delete中调用free,就完成了内存的释放操作 。
需要注意的是,在释放内存之前,operator delete会先检查指针p是否为NULL 。如果p是NULL,说明没有实际的内存需要释放,operator delete会直接返回,不执行任何操作 。这是一种常见的安全检查机制,避免了对空指针进行无效的释放操作,防止程序出现未定义行为 。
三、为什么要重载 operator new 和 operator delete
(一)优化内存分配效率
在程序运行过程中,频繁的内存申请和释放操作会带来不小的开销。以链表节点的内存分配为例,如果每次创建和销毁链表节点都使用默认的operator new和operator delete ,就会频繁地与操作系统的内存管理机制交互,导致效率低下。
假设我们有一个链表,链表中的节点频繁地被创建和删除。在默认情况下,每次创建一个节点,operator new都会向操作系统申请内存 ,而每次删除节点,operator delete又会将内存归还给操作系统 。这一来一回的操作不仅涉及系统调用,还可能导致内存碎片化,降低内存的使用效率。
通过重载operator new和operator delete ,我们可以实现内存池技术 。内存池就像是一个预先准备好的内存仓库,我们在程序开始时一次性向操作系统申请一大块内存,将其作为内存池 。当需要创建链表节点时,从内存池中分配内存,而不是每次都向操作系统申请;当节点被销毁时,将内存放回内存池,而不是归还给操作系统 。这样,就避免了频繁的系统调用,大大提高了内存分配和释放的效率 。例如:
#include <memory>
#include <iostream>
struct ListNode {
ListNode* _prev;
ListNode* _next;
int _data;
// 重载operator new
void* operator new(size_t size) {
static std::allocator<ListNode> alloc;
void* p = alloc.allocate(1);
std::cout << "从内存池分配内存" << std::endl;
return p;
}
// 重载operator delete
void operator delete(void* p) {
static std::allocator<ListNode> alloc;
alloc.deallocate((ListNode*)p, 1);
std::cout << "将内存放回内存池" << std::endl;
}
};
在这个例子中,ListNode结构体重载了operator new和operator delete 。在operator new中,使用std::allocator从内存池中分配内存 ;在operator delete中,将内存归还给内存池 。通过这种方式,链表节点的内存分配和释放效率得到了显著提升 。
(二)实现自定义内存管理策略
在一些特定的应用场景中,默认的内存管理策略无法满足需求,这时就需要重载operator new和operator