C++ 内存管理

1. C/C++ 程序内存区域划分

程序在运行时,操作系统会将其内存划分为不同的区域,每个区域有特定的用途。

  • 内核空间 (Kernel Space): 用户代码不能读写,受保护的系统区域。
  • 栈 (Stack): 向下增长。存储非静态局部变量、函数参数、返回值等。
  • 内存映射段 (Memory Mapping Segment): 用于文件映射、动态库加载、匿名映射。是高效的I/O映射方式。
  • 堆 (Heap): 向上增长。用于程序运行时动态内存分配 (malloc/new)。
  • 数据段 (Data Segment): 存储全局数据和静态数据 (static variables)。
  • 代码段 (Code Segment): 存储可执行代码和只读常量 (如字符串常量)。
cpp 复制代码
int globalVar = 1;                 // 数据段
static int staticGlobalVar = 1;    // 数据段

void Test() {
    static int staticVar = 1;      // 数据段
    int localVar = 1;              // 栈
    
    int num1[10] = {1, 2, 3, 4};   // num1数组本身在栈
    
    char char2[] = "abcd";         // char2数组在栈,内容从常量区拷贝过来
    const char* pChar3 = "abcd";   // pChar3指针在栈,指向代码段(常量区)
    
    int* ptr1 = (int*)malloc(4);   // ptr1指针在栈,指向堆
    
    free(ptr1);
}

2. C语言动态内存管理

C语言主要通过四个函数进行动态内存管理。

  • malloc(size_t size): 分配指定字节大小的内存,不初始化(内容为随机值)。
  • calloc(size_t num, size_t size): 分配内存并将内容初始化为 0。
  • realloc(void* ptr, size_t size): 调整已分配内存块的大小。
  • free(void* ptr): 释放内存。

3. C++ 动态内存管理 (new / delete)

C++ 引入了 `new` 和 `delete` 操作符,不仅能分配内存,还能自动处理对象的构造和析构。

3.1 使用方法

  • 单个对象: new Type (申请), delete ptr (释放)。
  • 数组: new Type[N] (申请), delete[] ptr (释放)。
  • 初始化: new int(10) (初始化为10)。

3.2 自定义类型 (核心优势)

对于自定义类型 (class/struct):

  • new: 先开辟空间,然后自动调用构造函数
  • delete: 先自动调用析构函数,然后释放空间。
  • malloc/free: 只负责开辟/释放空间,不调用构造和析构函数。
cpp 复制代码
class A {
public:
    A(int a = 0) : _a(a) { cout << "A()" << endl; }
    ~A() { cout << "~A()" << endl; }
private:
    int _a;
};

int main() {
    A* p1 = new A(1);   // 分配内存 + 调用构造函数
    delete p1;          // 调用析构函数 + 释放内存
    
    A* p2 = (A*)malloc(sizeof(A)); // 仅分配内存
    free(p2);           // 仅释放内存
}

4. operator new 与 operator delete

newdelete 是操作符,而 operator newoperator delete 是系统提供的全局函数。

4.1 实现机制

  • operator new: 底层通过循环调用 malloc 申请空间。如果失败,尝试执行空间不足应对措施;如果没有措施,抛出 bad_alloc 异常。
  • operator delete: 底层通过调用 free 释放空间。

注意:我们一般不直接调用这两个函数,而是使用 new 关键字,编译器会自动生成调用它们的代码。

4.2 重载 (了解)

可以重载这两个函数来实现特殊的内存管理需求(如内存池、内存泄漏检测日志)。

5. new 和 delete 的底层实现原理

5.1 内置类型

对于 int, char 等内置类型,new/deletemalloc/free 基本一致。主要区别在于 new 失败会抛异常,而 malloc 返回 NULL。

5.2 自定义类型 (T)

  • new T: 调用 operator new -> 调用构造函数。
  • delete p: 调用析构函数 -> 调用 operator delete
  • new T[N]: 调用 operator new[] (其内部调用 operator new) -> 执行 N 次构造函数。
  • delete[] p: 执行 N 次析构函数 -> 调用 operator delete[] (其内部调用 operator delete)。

6. 定位 new 表达式 (Placement-new)

定位 new 允许我们在已分配的原始内存空间 中调用构造函数初始化一个对象。通常配合内存池使用。

语法

new (place_address) Type(initializer-list);

使用步骤

  1. 分配内存 (如使用 malloc 或 内存池)。
  2. 使用定位 new 显示调用构造函数。
  3. 显示调用析构函数 (显式写法: p->~A())。
  4. 释放内存 (如 free)。
cpp 复制代码
// 模拟:p1 是一块已经分配好的内存
A* p1 = (A*)malloc(sizeof(A));

// 在 p1 指向的内存上构造对象 A
new(p1) A(10); 

// 必须显式调用析构函数
p1->~A();

// 最后释放物理内存
free(p1);
相关推荐
黄晓琪2 小时前
Java AQS底层原理:面试深度解析(附实战避坑)
java·开发语言·面试
姓蔡小朋友2 小时前
Java 定时器
java·开发语言
恒者走天下2 小时前
研一、大一大二学计算机应该怎么规划
c++
百锦再2 小时前
python之路并不一马平川:带你踩坑Pandas
开发语言·python·pandas·pip·requests·tools·mircro
灏瀚星空2 小时前
基于 Python 与 GitHub,打造个人专属本地化思维导图工具全流程方案(上)
开发语言·人工智能·经验分享·笔记·python·个人开发·visual studio
是Dream呀2 小时前
Python从0到100(一百):基于Transformer的时序数据建模与实现详解
开发语言·python·transformer
我是一只小青蛙8882 小时前
Windows下MATLAB与C++混合编程实战
c++
草莓熊Lotso2 小时前
Python 入门超详细指南:环境搭建 + 核心优势 + 应用场景(零基础友好)
运维·开发语言·人工智能·python·深度学习·学习·pycharm
*TQK*2 小时前
Python中as 的作用
开发语言·python