在C/C++开发中,内存管理是最基础也是最重要的技能之一。理解内存分配机制不仅能帮助我们编写更高效的代码,还能避免常见的内存错误。C和C++提供了两套不同的内存管理机制:C风格的malloc/free和C++风格的new/delete。
程序运行时需要的内存主要来自以下几个区域:
在C中,我们使用malloc和free来管理堆内存;而在C++中,除了可以使用malloc/free外,还引入了new和delete操作符。
| 特性 | malloc/free | new/delete |
|---|---|---|
| 来源 | C标准库函数 | C++操作符 |
| 类型安全 | 返回void*,需手动转换 | 返回正确类型指针 |
| 大小计算 | 手动指定字节数 | 自动计算 |
| 构造/析构 | 不调用 | 自动调用 |
| 失败处理 | 返回NULL | 抛出std::bad_alloc |
| 可重载 | 否 | 是 |
| 内存来源 | 堆(heap) | 自由存储区(free store) |
注意:虽然new/delete和malloc/free都用于动态内存管理,但它们的实现机制和适用场景有很大不同,混用可能导致严重问题。
当调用malloc(size)时,内存分配器实际分配的内存比请求的size要大,因为它需要额外的空间来存储管理信息:
c复制malloc(100)实际分配的内存块:
┌──────────────────┬──────────────────────────────────────────┐
│ Chunk Header │ 用户数据区域 │
│ (元数据) │ (100 bytes) │
├──────────────────┼──────────────────────────────────────────┤
│ size │ flags │ │
│ 8-16 bytes │ ← malloc()返回这个地址 │
└──────────────────┴──────────────────────────────────────────┘
free(p)的工作原理:
现代系统中的内存分配是一个多层次的过程:
code复制┌─────────────────────────────────────────────────────────────┐
│ 用户代码 │
│ malloc(100) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ C库分配器(glibc ptmalloc/musl) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 小块(<128KB): bins/tcache管理 │ │
│ │ - fastbin: 小于64字节的频繁分配 │ │
│ │ - smallbin/largebin: 较大的块 │ │
│ │ - tcache: 线程本地缓存(glibc 2.26+) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 大块(>=128KB): 直接mmap │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 系统调用 │
│ brk()/sbrk() - 扩展进程堆空间 │
│ mmap() - 映射匿名内存页 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 操作系统内核 │
│ - 分配虚拟地址空间 │
│ - 页表管理 │
│ - 缺页中断时分配物理内存 │
└─────────────────────────────────────────────────────────────┘
理解虚拟内存和物理内存的区别对内存管理至关重要:
cpp复制#include <iostream>
#include <cstdlib>
#include <cstring>
int main() {
size_t size = 100 * 1024 * 1024; // 100MB
std::cout << "1. 调用malloc(100MB)...\n";
char* p = (char*)malloc(size);
if (!p) {
std::cout << "分配失败\n";
return 1;
}
std::cout << "2. malloc返回成功\n";
std::cout << " 此时只分配了虚拟地址空间\n";
std::cout << " 物理内存尚未分配(按需分配)\n\n";
std::cout << "3. 写入数据,触发缺页中断...\n";
// 每4KB(一页)写一个字节
for (size_t i = 0; i < size; i += 4096) {
p[i] = 'A'; // 触发page fault,分配物理页
}
std::cout << " 写入完成,物理内存已分配\n\n";
std::cout << "4. 调用free()...\n";
free(p);
std::cout << " 内存归还给C库(不一定归还给OS)\n";
return 0;
}
关键概念:
code复制进程地址空间:
┌─────────────────────────────────────┐ 高地址
│ 内核空间 │
├─────────────────────────────────────┤ 0xC0000000 (32-bit)
│ 栈 ↓ │
│ ... │
│ │
│ 堆 ↑ │
├─────────────────────────────────────┤ ← brk指针
│ BSS段 │
├─────────────────────────────────────┤
│ 数据段 │
├─────────────────────────────────────┤
│ 代码段 │
└─────────────────────────────────────┘ 低地址
free后的内存去向取决于分配的大小:
cpp复制#include <iostream>
#include <cstdlib>
int main() {
// 小块内存
std::cout << "=== 小块内存 ===\n";
char* p1 = (char*)malloc(32);
std::cout << "p1 = " << (void*)p1 << "\n";
free(p1);
char* p2 = (char*)malloc(32);
std::cout << "p2 = " << (void*)p2 << "\n";
std::cout << "很可能p1 == p2(内存复用)\n\n";
free(p2);
// 大块内存
std::cout << "=== 大块内存(256KB) ===\n";
char* p3 = (char*)malloc(256 * 1024);
std::cout << "p3 = " << (void*)p3 << "\n";
free(p3); // 大块通常直接munmap,归还给OS
char* p4 = (char*)malloc(256 * 1024);
std::cout << "p4 = " << (void*)p4 << "\n";
std::cout << "大块每次分配地址可能不同\n";
free(p4);
return 0;
}
free的行为总结:
| 内存大小 | free行为 | 说明 |
|---|---|---|
| 小块(<128KB) | 放入空闲链表 | 供下次malloc复用 |
| 大块(>=128KB) | munmap归还OS | 虚拟地址立即失效 |
new表达式实际上做了两件事:分配内存和调用构造函数。
cpp复制T* p = new T(args...);
// 编译器展开为:
T* p;
void* mem = operator new(sizeof(T)); // 步骤1: 分配内存
try {
p = new (mem) T(args...); // 步骤2: placement new调用构造函数
} catch (...) {
operator delete(mem); // 构造失败则释放内存
throw;
}
new T(args)执行流程:
code复制┌─────────────────────────────────────────────────────────────┐
│ 步骤1: operator new(sizeof(T)) │
│ - 分配sizeof(T)字节的原始内存 │
│ - 内存未初始化 │
│ - 失败抛出std::bad_alloc │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 步骤2: 在分配的内存上调用构造函数 │
│ - new (mem) T(args...) (placement new语法) │
│ - 初始化成员变量 │
│ - 构造失败会自动调用operator delete │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 步骤3: 返回T*类型指针 │
│ - 类型安全,无需强制转换 │
└─────────────────────────────────────────────────────────────┘
delete表达式也做了两件事:调用析构函数和释放内存。
cpp复制delete p;
// 编译器展开为:
if (p != nullptr) {
p->~T(); // 步骤1: 调用析构函数
operator delete(p); // 步骤2: 释放内存
}
delete p执行流程:
code复制┌─────────────────────────────────────────────────────────────┐
│ 步骤1: 调用析构函数p->~T() │
│ - 释放对象持有的资源 │
│ - 对于nullptr,整个delete是空操作 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 步骤2: operator delete(p) │
│ - 释放原始内存 │
│ - 通常内部调用free() │
└─────────────────────────────────────────────────────────────┘
cpp复制#include <iostream>
#include <new>
class Widget {
public:
int id;
Widget(int i) : id(i) {
std::cout << " 2. 构造函数: Widget(" << id << ") this=" << this << "\n";
}
~Widget() {
std::cout << " 1. 析构函数: ~Widget() this=" << this << "\n";
}
// 重载类的operator new/delete
static void* operator new(size_t size) {
std::cout << " 1. operator new: 请求" << size << "字节\n";
void* p = ::operator new(size);
std::cout << " 返回地址: " << p << "\n";
return p;
}
static void operator delete(void* p) noexcept {
std::cout << " 2. operator delete: 释放" << p << "\n";
::operator delete(p);
}
};
int main() {
std::cout << "=== new Widget(42) ===\n";
Widget* w = new Widget(42);
std::cout << "\n=== delete w ===\n";
delete w;
return 0;
}
输出:
code复制=== new Widget(42) ===
1. operator new: 请求4字节
返回地址: 0x55f8a1b2c3d0
2. 构造函数: Widget(42) this=0x55f8a1b2c3d0
=== delete w ===
1. 析构函数: ~Widget() this=0x55f8a1b2c3d0
2. operator delete: 释放0x55f8a1b2c3d0
数组形式的new和delete有特殊的处理方式:
cpp复制#include <iostream>
class Item {
public:
int id;
Item() : id(counter++) {
std::cout << "Item(" << id << ")构造\n";
}
~Item() {
std::cout << "Item(" << id << ")析构\n";
}
static int counter;
};
int Item::counter = 0;
int main() {
std::cout << "=== new Item[3] ===\n";
Item* arr = new Item[3];
std::cout << "\n=== delete[] arr ===\n";
delete[] arr;
return 0;
}
new[]的内存布局:
code复制new T[n]分配的内存:
┌──────────────────┬─────────┬─────────┬─────────┬─────────┐
│ 元素个数(size_t) │ T[0] │ T[1] │ T[2] │ ... │
│ (编译器插入) │ │ │ │ │
└──────────────────┴─────────┴─────────┴─────────┴─────────┘
↑
new[]返回这里
delete[]的工作流程:
重要提示:使用new[]分配的内存必须用delete[]释放,使用new分配的内存必须用delete释放,混用会导致未定义行为。
在
cpp复制// 普通版本(分配失败抛异常)
void* operator new(std::size_t size);
void* operator new[](std::size_t size);
// nothrow版本(分配失败返回nullptr)
void* operator new(std::size_t size, const std::nothrow_t&) noexcept;
void* operator new[](std::size_t size, const std::nothrow_t&) noexcept;
// placement new(不分配内存,只返回传入的指针)
void* operator new(std::size_t size, void* ptr) noexcept;
void* operator new[](std::size_t size, void* ptr) noexcept;
// 释放函数
void operator delete(void* ptr) noexcept;
void operator delete[](void* ptr) noexcept;
void operator delete(void* ptr, std::size_t size) noexcept; // C++14 sized delete
我们可以替换全局的operator new/delete来实现自定义的内存管理策略:
cpp复制#include <iostream>
#include <cstdlib>
#include <new>
#include <atomic>
// 全局统计
std::atomic<size_t> g_total_allocated{0};
std::atomic<size_t> g_total_freed{0};
std::atomic<size_t> g_allocation_count{0};
// 替换全局operator new
void* operator new(std::size_t size) {
if (size == 0) size = 1; // C++标准要求
void* p = std::malloc(size);
if (!p) {
throw std::bad_alloc();
}
g_total_allocated += size;
++g_allocation_count;
std::cout << "[new] " << size << " bytes at " << p
<< " (total: " << g_total_allocated << ")\n";
return p;
}
// 替换全局operator delete
void operator delete(void* p) noexcept {
if (p) {
std::cout << "[delete] " << p << "\n";
std::free(p);
}
}
// C++14 sized delete
void operator delete(void* p, std::size_t size) noexcept {
if (p) {
g_total_freed += size;
std::cout << "[delete] " << p << " (" << size << " bytes)\n";
std::free(p);
}
}
// 数组版本
void* operator new[](std::size_t size) {
return operator new(size);
}
void operator delete[](void* p) noexcept {
operator delete(p);
}
void operator delete[](void* p, std::size_t size) noexcept {
operator delete(p, size);
}
int main() {
std::cout << "=== 分配int ===\n";
int* p1 = new int(42);
delete p1;
std::cout << "\n=== 分配int[5] ===\n";
int* p2 = new int[5];
delete[] p2;
std::cout << "\n=== 分配string ===\n";
std::string* s = new std::string("Hello, World!");
delete s;
std::cout << "\n=== 统计 ===\n";
std::cout << "总分配: " << g_total_allocated << " bytes\n";
std::cout << "分配次数: " << g_allocation_count << "\n";
return 0;
}
除了替换全局版本,我们还可以为特定类重载operator new/delete:
cpp复制#include <iostream>
#include <cstddef>
#include <new>
#include <array>
class PooledObject {
private:
static constexpr size_t POOL_SIZE = 8;
static constexpr size_t OBJ_SIZE = sizeof(PooledObject);
// 简单的内存池
struct Pool {
std::array<std::byte, OBJ_SIZE * POOL_SIZE> memory;
std::array<bool, POOL_SIZE> used{};
size_t allocated = 0;
};
static Pool pool_;
public:
int data;
PooledObject(int d = 0) : data(d) {
std::cout << " 构造PooledObject(" << data << ")\n";
}
~PooledObject() {
std::cout << " 析构PooledObject(" << data << ")\n";
}
// 类特定operator new
static void* operator new(size_t size) {
std::cout << " [Pool::new]请求" << size << "字节\n";
if (size != OBJ_SIZE) {
// 大小不匹配,使用全局new
return ::operator new(size);
}
for (size_t i = 0; i < POOL_SIZE; ++i) {
if (!pool_.used[i]) {
pool_.used[i] = true;
++pool_.allocated;
void* p = pool_.memory.data() + i * OBJ_SIZE;
std::cout << " 分配槽位" << i << "地址" << p
<< " (池使用: " << pool_.allocated << "/" << POOL_SIZE << ")\n";
return p;
}
}
throw std::bad_alloc();
}
// 类特定operator delete
static void operator delete(void* p) noexcept {
if (!p) return;
std::byte* bp = static_cast<std::byte*>(p);
std::byte* pool_start = pool_.memory.data();
std::byte* pool_end = pool_start + OBJ_SIZE * POOL_SIZE;
if (bp >= pool_start && bp < pool_end) {
size_t index = (bp - pool_start) / OBJ_SIZE;
pool_.used[index] = false;
--pool_.allocated;
std::cout << " [Pool::delete]释放槽位" << index
<< " (池使用: " << pool_.allocated << "/" << POOL_SIZE << ")\n";
} else {
::operator delete(p);
}
}
};
// 静态成员定义
PooledObject::Pool PooledObject::pool_;
int main() {
std::cout << "=== 从池分配 ===\n";
PooledObject* p1 = new PooledObject(1);
PooledObject* p2 = new PooledObject(2);
PooledObject* p3 = new PooledObject(3);
std::cout << "\n=== 释放p2 ===\n";
delete p2;
std::cout << "\n=== 再分配(复用p2的槽位)===\n";
PooledObject* p4 = new PooledObject(4);
std::cout << "\n=== 清理 ===\n";
delete p1;
delete p3;
delete p4;
return 0;
}
输出:
code复制=== 从池分配 ===
[Pool::new]请求4字节
分配槽位0地址0x... (池使用: 1/8)
构造PooledObject(1)
[Pool::new]请求4字节
分配槽位1地址0x... (池使用: 2/8)
构造PooledObject(2)
[Pool::new]请求4字节
分配槽位2地址0x... (池使用: 3/8)
构造PooledObject(3)
=== 释放p2 ===
析构PooledObject(2)
[Pool::delete]释放槽位1 (池使用: 2/8)
=== 再分配(复用p2的槽位)===
[Pool::new]请求4字节
分配槽位1地址0x... (池使用: 3/8)
构造PooledObject(4)
=== 清理 ===
析构PooledObject(1)
[Pool::delete]释放槽位0 (池使用: 2/8)
析构PooledObject(3)
[Pool::delete]释放槽位2 (池使用: 1/8)
析构PooledObject(4)
[Pool::delete]释放槽位1 (池使用: 0/8)
Placement new是一种特殊形式的new表达式,它不分配内存,只在指定的地址上调用构造函数:
cpp复制#include <iostream>
#include <new>
class Widget {
public:
int x, y;
Widget(int a, int b) : x(a), y(b) {
std::cout << "Widget(" << x << ", " << y << ") at " << this << "\n";
}
~Widget() {
std::cout << "~Widget() at " << this << "\n";
}
};
int main() {
// 1. 预分配内存(栈上)
alignas(Widget) unsigned char buffer[sizeof(Widget)];
std::cout << "Buffer地址: " << (void*)buffer << "\n";
std::cout << "sizeof(Widget) = " << sizeof(Widget) << "\n\n";
// 2. Placement new: 在buffer上构造对象
Widget* w = new (buffer) Widget(10, 20);
std::cout << "w->x = " << w->x << ", w->y = " << w->y << "\n\n";
// 3. 必须手动调用析构函数!
w->~Widget();
// 4. 可以在同一位置再次构造
Widget* w2 = new (buffer) Widget(30, 40);
w2->~Widget();
// ⚠️ 绝对不能delete w!buffer是栈内存
// delete w; // 未定义行为!
return 0;
}
Placement new的语法结构如下:
cpp复制new (地址) 类型(参数...)
│ │ │ │
│ │ │ └── 传给构造函数的参数
│ │ └─────── 要构造的类型
│ └────────────── placement new的地址参数
└─────────────────── new关键字
// 实际调用:
void* operator new(size_t size, void* ptr) noexcept {
return ptr; // 什么都不做,直接返回传入的指针
}
Placement new在实际开发中有很多应用场景,比如实现类似std::optional的功能:
cpp复制#include <iostream>
#include <new>
#include <utility>
template <typename T>
class SimpleOptional {
private:
alignas(T) unsigned char storage_[sizeof(T)];
bool has_value_ = false;
T* ptr() { return reinterpret_cast<T*>(storage_); }
const T* ptr() const { return reinterpret_cast<const T*>(storage_); }
public:
SimpleOptional() = default;
SimpleOptional(const T& value) : has_value_(true) {
new (storage_) T(value); // placement new
}
SimpleOptional(T&& value) : has_value_(true) {
new (storage_) T(std::move(value));
}
~SimpleOptional() {
if (has_value_) {
ptr()->~T(); // 手动析构
}
}
// 禁止拷贝(简化实现)
SimpleOptional(const SimpleOptional&) = delete;
SimpleOptional& operator=(const SimpleOptional&) = delete;
bool has_value() const { return has_value_; }
T& value() { return *ptr(); }
const T& value() const { return *ptr(); }
template <typename... Args>
T& emplace(Args&&... args) {
if (has_value_) {
ptr()->~T();
}
new (storage_) T(std::forward<Args>(args)...);
has_value_ = true;
return *ptr();
}
void reset() {
if (has_value_) {
ptr()->~T();
has_value_ = false;
}
}
};
class Resource {
public:
std::string name;
Resource(const std::string& n) : name(n) {
std::cout << "Resource(\"" << name << "\")构造\n";
}
~Resource() {
std::cout << "Resource(\"" << name << "\")析构\n";
}
};
int main() {
std::cout << "=== 创建空optional ===\n";
SimpleOptional<Resource> opt;
std::cout << "has_value: " << opt.has_value() << "\n\n";
std::cout << "=== emplace ===\n";
opt.emplace("First");
std::cout << "has_value: " << opt.has_value() << "\n";
std::cout << "value: " << opt.value().name << "\n\n";
std::cout << "=== emplace again(替换) ===\n";
opt.emplace("Second");
std::cout << "value: " << opt.value().name << "\n\n";
std::cout << "=== reset ===\n";
opt.reset();
std::cout << "has_value: " << opt.has_value() << "\n\n";
std::cout << "=== opt离开作用域 ===\n";
return 0;
}
输出:
code复制=== 创建空optional ===
has_value: 0
=== emplace ===
Resource("First")构造
has_value: 1
value: First
=== emplace again(替换) ===
Resource("First")析构
Resource("Second")构造
value: Second
=== reset ===
Resource("Second")析构
has_value: 0
=== opt离开作用域 ===
另一个使用placement new的典型场景是实现无锁环形队列:
cpp复制#include <iostream>
#include <new>
#include <atomic>
#include <thread>
#include <utility>
template <typename T, size_t Capacity>
class LockFreeQueue {
static_assert((Capacity & (Capacity - 1)) == 0, "Capacity必须是2的幂");
private:
alignas(64) std::atomic<size_t> head_{0}; // 消费者读取位置
alignas(64) std::atomic<size_t> tail_{0}; // 生产者写入位置
alignas(64) alignas(T) unsigned char storage_[sizeof(T) * Capacity];
T* at(size_t index) {
return reinterpret_cast<T*>(storage_) + (index & (Capacity - 1));
}
public:
~LockFreeQueue() {
// 析构所有剩余元素
size_t head = head_.load(std::memory_order_relaxed);
size_t tail = tail_.load(std::memory_order_relaxed);
while (head != tail) {
at(head)->~T();
++head;
}
}
template <typename U>
bool push(U&& value) {
size_t tail = tail_.load(std::memory_order_relaxed);
size_t next_tail = tail + 1;
// 检查是否满
if (next_tail - head_.load(std::memory_order_acquire) > Capacity) {
return false;
}
// Placement new构造元素
new (at(tail)) T(std::forward<U>(value));
// 发布新的tail
tail_.store(next_tail, std::memory_order_release);
return true;
}
bool pop(T& value) {
size_t head = head_.load(std::memory_order_relaxed);
// 检查是否空
if (head == tail_.load(std::memory_order_acquire)) {
return false;
}
// 移动并析构
T* ptr = at(head);
value = std::move(*ptr);
ptr->~T(); // 手动析构
// 发布新的head
head_.store(head + 1, std::memory_order_release);
return true;
}
};
int main() {
LockFreeQueue<std::string, 8> queue;
std::thread producer([&]() {
for (int i = 0; i < 10; ++i) {
while (!queue.push("Message " + std::to_string(i))) {
std::this_thread::yield();
}
std::cout << "Produced: Message " << i << "\n";
}
});
std::thread consumer([&]() {
for (int i = 0; i < 10; ++i) {
std::string msg;
while (!queue.pop(msg)) {
std::this_thread::yield();
}
std::cout << "Consumed: " << msg << "\n";
}
});
producer.join();
consumer.join();
return 0;
}
混用new/delete和malloc/free会导致严重问题:
cpp复制#include <iostream>
#include <cstdlib>
class ManagedResource {
public:
int* data;
ManagedResource() : data(new int[100]) {
std::cout << "构造: 分配data\n";
}
~ManagedResource() {
std::cout << "析构: 释放data\n";
delete[] data;
}
};
void test_wrong_usage() {
std::cout << "=== 错误1: new + free ===\n";
ManagedResource* p1 = new ManagedResource();
// free(p1); // ❌ 不调用析构函数,data泄漏!
delete p1; // ✅ 正确
std::cout << "\n=== 错误2: malloc + delete ===\n";
ManagedResource* p2 = (ManagedResource*)malloc(sizeof(ManagedResource));
// p2->data是垃圾值,未初始化
// delete p2; // ❌ 对未构造对象调用析构函数!delete[]垃圾地址!
free(p2); // ✅ 正确(虽然对象从未初始化)
std::cout << "\n=== 正确: malloc + placement new + 手动析构 + free ===\n";
void* raw = malloc(sizeof(ManagedResource));
ManagedResource* p3 = new (raw) ManagedResource(); // 构造
p3->~ManagedResource(); // 析构
free(raw); // 释放
}
int main() {
test_wrong_usage();
return 0;
}
数组形式的new和delete必须配对使用:
cpp复制#include <iostream>
class Element {
public:
int id;
Element() : id(counter++) { std::cout << "Element(" << id << ")\n"; }
~Element() { std::cout << "~Element(" << id << ")\n"; }
static int counter;
};
int Element::counter = 0;
int main() {
std::cout << "=== new[] + delete[] (正确) ===\n";
Element* arr1 = new Element[3];
delete[] arr1; // 调用3次析构函数
std::cout << "\n=== new[] + delete (错误!) ===\n";
Element::counter = 0;
Element* arr2 = new Element[3];
// delete arr2; // ❌ 只析构arr2[0],未定义行为!
delete[] arr2; // ✅ 正确
std::cout << "\n=== new + delete (正确) ===\n";
Element::counter = 0;
Element* single = new Element;
delete single; // ✅
// delete[] single; // ❌ 未定义行为!
return 0;
}
构造函数中抛出异常时需要特别注意资源清理:
cpp复制#include <iostream>
#include <stdexcept>
#include <new>
class Problematic {
public:
int* data1;
int* data2;
Problematic(bool fail_second) {
std::cout << "构造开始\n";
data1 = new int[10];
std::cout << " data1分配成功\n";
if (fail_second) {
std::cout << " data2分配前抛异常\n";
delete[] data1; // 必须手动清理已分配的资源!
throw std::runtime_error("构造失败");
}
data2 = new int[10];
std::cout << " data2分配成功\n";
}
~Problematic() {
std::cout << "析构\n";
delete[] data2;
delete[] data1;
}
};
// 更好的方式:使用智能指针
#include <memory>
class Safe {
public:
std::unique_ptr<int[]> data1;
std::unique_ptr<int[]> data2;
Safe(bool fail_second) {
std::cout << "Safe构造开始\n";
data1 = std::make_unique<int[]>(10);
std::cout << " data1分配成功\n";
if (fail_second) {
std::cout << " 抛异常\n";
throw std::runtime_error("构造失败");
// data1自动释放!
}
data2 = std::make_unique<int[]>(10);
std::cout << " data2分配成功\n";
}
// 不需要手写析构函数
};
int main() {
std::cout << "=== Problematic (手动管理) ===\n";
try {
Problematic p(true);
} catch (const std::exception& e) {
std::cout << "捕获异常: " << e.what() << "\n";
}
std::cout << "\n=== Safe (智能指针) ===\n";
try {
Safe s(true);
} catch (const std::exception