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 TN: 调用 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);
相关推荐
anscos6 小时前
为CUDA 代码引入静态分析
c++·工业软件·功能检测
众少成多积小致巨6 小时前
C++ 规范参考(中)
c++
shylyly_7 小时前
C++中的类型转换
开发语言·c++·匿名对象·隐式类型转换·拷贝优化
jinyishu_7 小时前
哈希表原理与开放定址法C++实现
c++·哈希算法·散列表
cui_ruicheng7 小时前
Python数据分析(一):数据分析概述与环境搭建
开发语言·python·数据分析
心平气和量大福大8 小时前
C#-WPF-UserControl-生命周期(加载 退出)
开发语言·c#·wpf
小龙报8 小时前
【优选算法】1. 水果成蓝 2.找到字符串中所有字母的异位词
java·c语言·数据结构·数据库·c++·redis·算法
sunywz8 小时前
【c#】 Web Deploy一键发布,IIS部署全流程
开发语言·前端·c#
Brilliantwxx8 小时前
【Linux】 软件包管理器(yum)+ Vim使用
linux·运维·服务器·开发语言·编辑器·vim
雪的季节8 小时前
Python 线程同步与异步编程 全解析
java·开发语言