讲座主题:C++中的内存分配器——自定义new/delete的操作
大家好!欢迎来到今天的讲座。今天我们要聊聊一个既神秘又有趣的主题:C++中的内存分配器,以及如何自定义new
和delete
操作符。听起来很复杂?别担心,我会用轻松幽默的语言和通俗易懂的例子带你一步步深入了解。
为什么我们需要自定义内存分配?
在C++中,默认的new
和delete
已经为我们提供了方便的动态内存管理功能。但有时候,我们可能需要更精细地控制内存分配,比如:
- 性能优化:默认的内存分配器可能不够高效,特别是在频繁分配和释放小块内存的情况下。
- 调试用途:通过自定义内存分配器,我们可以检测内存泄漏或越界访问等问题。
- 特殊需求:某些嵌入式系统可能需要特定的内存管理策略。
接下来,我们就来看看如何实现这些目标。
自定义new和delete的基础知识
在C++中,new
和delete
实际上是两个独立的操作符。new
负责分配内存并调用构造函数,而delete
则负责调用析构函数并释放内存。
默认行为
先来看一下默认的new
和delete
是如何工作的:
class MyClass {
public:
MyClass() { std::cout << "Constructor calledn"; }
~MyClass() { std::cout << "Destructor calledn"; }
};
int main() {
MyClass* obj = new MyClass(); // 分配内存并调用构造函数
delete obj; // 调用析构函数并释放内存
}
输出:
Constructor called
Destructor called
自定义new和delete
现在,让我们看看如何自定义new
和delete
操作符。
重载全局new和delete
如果你想为整个程序提供一个统一的内存分配策略,可以重载全局的new
和delete
操作符。
#include <iostream>
#include <cstdlib>
void* operator new(std::size_t size) {
std::cout << "Custom new called, allocating " << size << " bytesn";
void* ptr = std::malloc(size);
if (!ptr) {
throw std::bad_alloc();
}
return ptr;
}
void operator delete(void* ptr) noexcept {
std::cout << "Custom delete calledn";
std::free(ptr);
}
int main() {
int* p = new int(42); // 使用自定义new
delete p; // 使用自定义delete
}
输出:
Custom new called, allocating 4 bytes
Custom delete called
小贴士:注意
operator delete
的签名必须包含noexcept
,否则会导致编译错误。
类级别的new和delete
如果你只想为某个类提供特殊的内存管理策略,可以在类内部重载new
和delete
。
class MyClass {
public:
void* operator new(std::size_t size) {
std::cout << "MyClass custom new called, allocating " << size << " bytesn";
return std::malloc(size);
}
void operator delete(void* ptr) noexcept {
std::cout << "MyClass custom delete calledn";
std::free(ptr);
}
};
int main() {
MyClass* obj = new MyClass(); // 使用MyClass的自定义new
delete obj; // 使用MyClass的自定义delete
}
输出:
MyClass custom new called, allocating 1 bytes
MyClass custom delete called
阵容强大的placement new
除了普通的new
,C++还提供了一种叫做placement new
的特殊形式。它允许我们在指定的内存地址上创建对象。
#include <new> // 必须包含这个头文件
class MyClass {
public:
MyClass() { std::cout << "Constructor calledn"; }
~MyClass() { std::cout << "Destructor calledn"; }
};
int main() {
char buffer[sizeof(MyClass)]; // 一块未初始化的内存
MyClass* obj = new (buffer) MyClass(); // 在buffer上构造对象
obj->~MyClass(); // 手动调用析构函数
}
输出:
Constructor called
Destructor called
警告:使用
placement new
时,记得手动调用析构函数,否则可能导致资源泄漏。
表格总结:不同new的比较
类型 | 描述 | 使用场景 |
---|---|---|
普通new | 动态分配内存并调用构造函数 | 通用动态内存分配 |
全局new | 为整个程序提供统一的内存分配策略 | 性能优化、调试 |
类级别new | 为特定类提供定制化的内存管理策略 | 嵌入式系统、特殊需求 |
placement new | 在指定内存地址上构造对象 | 对象池、内存复用 |
国外技术文档引用
- ISO C++ Standard:描述了
new
和delete
的基本语义,并规定了它们的行为。 - Scott Meyers《Effective C++》:深入探讨了自定义内存分配器的设计原则和最佳实践。
- Bjarne Stroustrup《The C++ Programming Language》:详细介绍了C++中内存管理的底层机制。
结语
今天我们聊了C++中自定义new
和delete
的操作。从全局到类级别,再到placement new
,每一种方式都有其独特的应用场景。希望这篇文章能帮助你更好地理解和掌握C++的内存管理技巧。
下一次讲座,我们将深入探讨C++中的智能指针。敬请期待!
谢谢大家!