C++ new & delete
C++ 的 new 和 delete 是动态内存核心,但现代代码中应避免裸用。理解其机制避免陷阱:new 分配内存+构造对象,delete 析构+释放内存。
Widget 示例类(全局唯一定义)
#include <print>
#include <format>
class Widget {
public:
Widget(int value) : value_(value) {
std::println("Widget constructed with value: {}", value_);
}
~Widget() {
std::println("Widget destroyed");
}
private:
int value_;
};new 表达式 vs operator new 函数
new 表达式(语言操作符,不可重载):分配内存(调用 operator new)+构造对象,失败抛 std::bad_alloc。
operator new(可重载函数):仅分配内存,返回 void*,类似 malloc。
// new 表达式(推荐日常)
Widget* p1 = new Widget(42); // operator new + 构造
delete p1; // 析构 + operator delete
// operator new(特殊用,如 placement new)
void* raw = operator new(sizeof(Widget));
Widget* p2 = new (raw) Widget(42); // placement new 构造
p2->~Widget(); // 手动析构
operator delete(raw); // 手动释放| 特性 | new 表达式 | operator new |
|---|---|---|
| 类型 | 操作符 | 函数 |
| 功能 | 分配+构造 | 仅分配 |
| 返回 | T* | void* |
| 重载 | 否 | 是 |
为什么区分:自定义分配(内存池)、placement new、调试拦截。
示例自定义:
void* operator new(std::size_t size) {
std::println("Allocating {} bytes", size);
void* p = std::malloc(size);
if (!p) throw std::bad_alloc{};
return p;
}
void operator delete(void* p) noexcept { std::free(p); }变体
数组
int* arr = new int[10](); // 值初始化为0
delete[] arr; // 必须配对 new[] + delete[]铁律:new[] 配 delete[],new 配 delete。混用 UB。
nothrow
#include <new>
int* p = new (std::nothrow) int[1'000'000'000];
if (!p) std::println("Failed");Placement new
alignas(Widget) unsigned char buf[sizeof(Widget)];
Widget* w = new (buf) Widget(42);
w->~Widget(); // 无 deletenew/delete vs malloc/free
| 特性 | new/delete | malloc/free |
|---|---|---|
| 类型安全 | 是 | 否 |
| 构造/析构 | 是 | 否 |
| 失败 | bad_alloc | nullptr |
| 重载 | 是 | 否 |
优先 new/delete,除非 C 接口。
失败处理
默认抛异常,可设 handler:
#include <new>
void handler() {
std::println("OOM!");
std::set_new_handler(nullptr);
throw std::bad_alloc{};
}
int main() { std::set_new_handler(handler); /* ... */ }或用 nothrow。
自定义(类/全局)
类成员重载示例:
class MyClass {
public:
void* operator new(std::size_t size) {
return ::operator new(size); // 调用全局
}
void operator delete(void* p) noexcept {
::operator delete(p);
}
};C++17 对齐:用 std::align_val_t。
陷阱 & 最佳实践
- 泄漏:忘 delete。解:智能指针。
- 重复 delete / 野指针:设
nullptr后 delete。 - 悬挂指针:delete 后别用。
- 异常安全:构造函数抛异常,内存泄漏。解:
std::make_unique。
错误:
Widget* p = new Widget(42);
risky(); // 抛异常,泄漏
delete p;正确:
auto p = std::make_unique<Widget>(42);
risky(); // 自动释放现代 C++(优先!)
智能指针(<memory>):
auto u = std::make_unique<Widget>(42); // 独占
auto s = std::make_shared<Widget>(42); // 共享,性能优make_ 胜过 new:异常安全,一次分配。
容器:
std::vector<Widget> vec; // 自动管理
vec.emplace_back(42);RAII:资源生命周期绑对象。
class RAIIResource {
void* res_;
public:
RAIIResource() : res_(acquire()) {}
~RAIIResource() { release(res_); }
RAIIResource(const RAIIResource&) = delete;
RAIIResource(RAIIResource&& o) noexcept : res_(o.res_) { o.res_ = nullptr; }
// ...
};性能 & 调试
- 内存池:高频小对象用,重载 operator new。
- 对齐:
alignas(64)缓存友好。 - 工具:Valgrind (
--leak-check=full),ASan (-fsanitize=address)。
总结
- 配对:new/delete,new[]/delete[]。
- 避免裸 new:用 unique_ptr/shared_ptr/vector。
- RAII:自动管理。
- 自定义慎用:遵循匹配规则。
现代 C++:裸 new/delete 是例外。智能指针+容器=零泄漏、零手动。
// ✅
auto p = std::make_unique<Widget>(42);
// ❌ 垃圾
Widget* p = new Widget(42); delete p;









