L8.3 - 堆栈内存分配深度解析(拓展)


🎯 教学目标

  1. 掌握内存布局原理:理解栈、堆、静态区的存储机制
  2. 精通动态内存管理:深入理解new/delete的底层实现
  3. 规避内存操作陷阱:识别常见内存错误模式
  4. 优化内存使用效率:掌握内存池等高级技巧

📚 核心知识点

一、内存区域全景图


二、栈内存 vs 堆内存对比

特性 栈内存 堆内存
管理方式 编译器自动管理 程序员手动管理
分配速度 纳秒级(移动栈指针) 微秒级(搜索空闲块)
容量限制 较小(默认MB级) 较大(受系统物理内存限制)
生命周期 作用域结束自动释放 显式释放前一直存在
内存碎片 无 可能产生
访问安全 自动边界检查(部分编译器) 无保护
典型应用 局部变量、函数参数 动态数据结构、大对象

三、最基础用法

1. 创建单个对象

1
2
3
4
5
6
7
8
9
// 创建对象
Person* p = new Person();

// 使用对象
p->sayHello();

// 释放对象
delete p; // 必须配对使用
p = nullptr; // 防止野指针

2. 创建对象数组

1
2
3
4
5
6
7
8
9
// 创建数组(10个元素)
int* nums = new int[10];

// 使用数组
nums[0] = 100;

// 释放数组
delete[] nums; // 必须用 delete[]
nums = nullptr;

四、正确配对使用(必须记住!)

创建方式 释放方式 错误示例
new 类型 delete delete[] p ❌
new 类型[数量] delete[] delete arr ❌
1
2
3
4
5
6
7
8
9
10
11
12
13
// ✅ 正确示例
string* s = new string("hello");
delete s;

int* arr = new int[5]{1,2,3};
delete[] arr;

// ❌ 错误示例
int* p = new int;
delete[] p; // 会导致崩溃

string* arr = new string[3];
delete arr; // 只释放第一个元素

五、现代更安全的做法

1. 智能指针(自动释放)

1
2
3
4
5
6
7
8
#include <memory>

// 单个对象(自动释放)
auto p = std::make_unique<Person>();

// 数组(自动释放)
auto nums = std::make_unique<int[]>(10);
nums[0] = 100; // 自动释放,无需手动delete

2. 使用标准容器(推荐)

1
2
3
4
5
6
7
8
#include <vector>

// 代替动态数组
std::vector<int> nums(10);
nums[0] = 100; // 自动管理内存

// 二维数组
std::vector<std::vector<int>> matrix(5, std::vector<int>(10));

六、必须注意的常见错误

1. 忘记释放内存

1
2
3
4
void test() {
int* p = new int(10);
// 忘记写 delete p → 内存泄漏!
}

2. 重复释放

1
2
3
int* p = new int;
delete p;
delete p; // ❌ 重复释放,程序崩溃

3. 访问已释放内存

1
2
3
int* p = new int(5);
delete p;
*p = 10; // ❌ p指向的内存已释放

七、最佳实践总结

  1. 配对使用原则

    • new → delete
    • new[] → delete[]
  2. 立即初始化习惯

    1
    int* p = new int(0); // ✅ 明确初始化
  3. 释放后置空指针

    1
    2
    delete p; 
    p = nullptr; // 防止误用
  4. 优先使用智能指针

    1
    auto p = std::make_unique<MyClass>(); // 自动管理
  5. 多用标准容器

    1
    std::vector<int> arr(100); // 自动扩容/释放

八、 与普通数组的关键区别

特性 普通数组 new/delete数组
分配位置 栈区 堆区
大小确定时机 编译期 运行期
可变大小 不可变 可动态调整(需重新分配)
初始化 可部分初始化 默认初始化(POD类型不初始化)
内存对齐 自动对齐 可定制对齐方式
异常安全 无异常 可能抛出bad_alloc

💡 实战应用场景

1. 动态矩阵类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Matrix {
double** data;
int rows, cols;
public:
Matrix(int r, int c) : rows(r), cols(c) {
data = new double*[rows];
for(int i=0; i<rows; ++i) {
data[i] = new double[cols]{0}; // 堆分配+零初始化
}
}

~Matrix() {
for(int i=0; i<rows; ++i) {
delete[] data[i]; // 逆序释放
}
delete[] data;
}
};

2. 内存池预分配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class MemoryPool {
char* pool;
size_t blockSize;
stack<void*> freeList;
public:
MemoryPool(size_t blockCount, size_t sizePerBlock)
: blockSize(sizePerBlock)
{
pool = new char[blockCount*sizePerBlock];
for(size_t i=0; i<blockCount; ++i) {
freeList.push(pool + i*sizePerBlock);
}
}

void* allocate() {
if(freeList.empty()) throw bad_alloc();
void* ptr = freeList.top();
freeList.pop();
return ptr;
}

void deallocate(void* ptr) {
freeList.push(ptr);
}
};

💣 深度陷阱解析

1. 内存泄漏模式

1
2
3
4
5
6
7
8
9
10
void leakyFunction() {
int* p = new int[100];
return; // 没有delete → 永久泄漏!
}

// 正确模式:RAII封装
void safeFunction() {
unique_ptr<int[]> p(new int[100]);
// 自动释放
}

2. 悬挂指针问题

1
2
3
4
5
6
7
8
int* createDangling() {
int local = 42;
return &local; // 返回栈地址 → 危险!
}

int* createValid() {
return new int(42); // 堆内存安全
}

3. 多态对象误删

1
2
3
4
5
class Base { virtual ~Base() {} };
class Derived : public Base {};

Base* p = new Derived;
delete p; // 正确:需要虚析构函数

🛠️ 调试技巧宝典

1. Valgrind内存检测

1
2
3
4
5
6
7
# 检测内存泄漏
valgrind --leak-check=full ./your_program

# 输出示例
==12345== 100 bytes in 1 blocks are definitely lost
==12345== at 0x483777F: operator new[](unsigned long)
==12345== by 0x401234: leakyFunction() (main.cpp:5)

2. 重载new/delete调试

1
2
3
4
5
6
7
8
9
10
// 跟踪内存分配
void* operator new(size_t size) {
cout << "Allocating " << size << " bytes\n";
return malloc(size);
}

void operator delete(void* ptr) noexcept {
cout << "Freeing memory\n";
free(ptr);
}

📊 性能优化指南

1. 对象池模式

1
2
3
4
5
6
7
8
9
10
11
12
class GameObject {
// ...
static MemoryPool pool;

void* operator new(size_t size) {
return pool.allocate();
}

void operator delete(void* ptr) {
pool.deallocate(ptr);
}
};

2. 智能指针最佳实践

1
2
3
4
5
6
7
8
9
10
// 现代C++安全用法
shared_ptr<int[]> createArray(size_t n) {
return make_shared<int[]>(n); // C++20起支持
}

// 二维数组安全封装
auto matrix = make_unique<unique_ptr<int[]>[]>(10);
for(int i=0; i<10; ++i) {
matrix[i] = make_unique<int[]>(20);
}