C++ --内存管理
C内存分区布局程序运行时内存被划分为几个逻辑区域内存区域存储内容生命周期特点栈函数局部变量、函数参数、返回值自动管理进入函数分配退出释放大小固定通常几MB访问最快溢出风险堆动态分配的对象new/delete手动管理直到显式释放大小受系统限制访问较慢可能产生碎片全局/静态区全局变量、static变量程序启动分配程序结束释放分为已初始化和未初始化常量区字符串常量、const修饰的全局数据程序启动分配程序结束释放通常只读试图修改会导致段错误代码区编译后的机器指令程序启动加载只读防止意外修改C内存管理方式通过new和delete操作符进行动态内存管理。一、管理内置类型int、数组等1. 单个内置类型变量#include iostream int main() { // 分配一个int初始化为10 int* p new int(10); std::cout *p std::endl; // 输出 10 // 使用完毕后释放 delete p; p nullptr; //置空防止野指针 return 0; }2. 内置类型数组#include iostream int main() { // 分配包含5个int的数组未初始化 int* arr new int[5]; // 赋值 for (int i 0; i 5; i) { arr[i] i * 10; } // 打印 for (int i 0; i 5; i) { std::cout arr[i] ; // 输出 0 10 20 30 40 } std::cout std::endl; // 释放数组内存注意是 delete[] delete[] arr; arr nullptr; return 0; }new[]必须配delete[]否则未定义行为可能内存泄漏或崩溃。二、管理自定义类型类对象1. 单个对象#include iostream #include string class Student { private: std::string name; int age; public: // 构造函数 Student(const std::string n, int a) : name(n), age(a) { std::cout 构造函数: name 被创建 std::endl; } // 析构函数 ~Student() { std::cout 析构函数: name 被销毁 std::endl; } void display() const { std::cout 姓名: name , 年龄: age std::endl; } }; int main() { // 在堆上创建Student对象 Student* s new Student(张三, 20); s-display(); // 输出: 姓名: 张三, 年龄: 20 // 释放对象先调用析构函数再释放内存 delete s; s nullptr; return 0; }输出顺序构造函数: 张三 被创建姓名: 张三, 年龄: 20析构函数: 张三 被销毁关键机制new做了两件事1.分配内存 2.调用构造函数delete也做两件事1. 调用析构函数 2.释放内存2. 自定义类型数组#include iostream #include string class Student { // ... 同上 ... }; int main() { // 分配包含3个Student对象的数组 // 注意这样会调用默认构造函数所以类必须有无参构造 Student* students new Student[3]{ {李四, 22}, // 可以用初始化列表 {王五, 23}, {赵六, 24} }; // 遍历访问 for (int i 0; i 3; i) { students[i].display(); } // 释放数组注意是 delete[] delete[] students; students nullptr; return 0; }new[]分配对象数组时要求类有默认构造函数如果类没有默认构造函数可以用new[] 初始化列表new/delete的底层机制一、new表达式的完整执行流程当你下new Student(张三, 20)时编译器会将其展开为三个步骤// 伪代码展示 new 的内部实现 Student* s new Student(张三, 20); // 等价于以下三步 // 步骤1调用 operator new 分配原始内存 void* raw_mem operator new(sizeof(Student)); // 步骤2在该内存上构造对象调用构造函数 Student* s new(raw_mem) Student(张三, 20); // placement new // 步骤3返回指针 return s;详细拆解步骤操作说明1. 内存分配operator new(sizeof(T))底层通常调用malloc()也可能重载2. 对象构造在原始内存上调用构造函数使用placement new3. 返回指针返回构造好的对象地址类型已转换为T*二、delete表达式的完整执行流程// 伪代码展示 delete 的内部实现 delete s; // 等价于以下两步 // 步骤1调用析构函数 s-~Student(); // 步骤2调用 operator delete 释放内存 operator delete(s);详细拆解步骤操作说明1. 对象析构调用析构函数~Student()释放对象内部持有的资源如动态成员2. 内存释放operator delete(ptr)底层通常调用free()也可能重载operator new与operator delete函数new和delete是用户进行动态内存申请和释放的操作符operator new和operator delete是 系统提供的全局函数new在底层调用operator new全局函数来申请空间delete在底层通operator delete全局函数来释放空间。数组版本new[]和delete[]数组版本特殊之处在于需要记录数组元素个数以便正确调用每个元素的析构函数。// new[] 的内部机制简化 int* arr new int[10]; // delete[] 会先读取元素个数然后依次调用析构函数最后释放内存 delete[] arr;实际分配的内存布局[元素个数 n][元素0] [元素1] ... [元素9]返回的是指向第一个元素的指针所以new[]必须配delete[]如果错用delete它不会读取元素个数只调用第一个元素的析构然后错误地释放整块内存。new/delete与malloc/free的对比对比维度new/deletemalloc/free本质C 运算符关键字C 标准库函数内存分配调用operator new→malloc直接调用malloc构造函数自动调用不调用析构函数自动调用不调用返回值类型类型安全T*void*需强制转换内存大小编译器自动计算手动指定sizeof失败处理抛出std::bad_alloc异常返回NULL是否可重载可以重载operator new不可重载分配数组new T[n]自动处理需手动计算总大小定位new表达式(placement-new)定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。使用格式new (place_address) type或者new (place_address) type(initializer-list) place_address必须是一个指针initializer-list是类型的初始化列表。使用场景内存池预先申请大块内存然后用 placement new 逐个构造对象。因为内存池分配出的内存没有初始化所以如果是自定义类型的对象需要使用new的定义表达进行显示调构造函数进行初始化。#include iostream #include new // 必须包含 class Widget { int data; public: Widget(int d) : data(d) { std::cout 构造 Widget, data data std::endl; } ~Widget() { std::cout 析构 Widget std::endl; } }; int main() { // 1. 先分配原始内存 void* raw_mem operator new(sizeof(Widget)); // 2. 在原始内存上构造对象placement new Widget* w new (raw_mem) Widget(42); // 3. 使用对象 // ... // 4. 显式调用析构注意不能 delete因为内存不是 new 分配的 w-~Widget(); // 5. 释放原始内存 operator delete(raw_mem); return 0; }关键代码void* raw_mem operator new(sizeof(Widget));这行做了什么调用operator new分配原始内存不构造任何对象返回void*指针指向一块sizeof(Widget)大小的未初始化内存这块内存是原始的里面是随机字节类似malloc的行为内存状态raw_mem 指向的 4/8/... 字节 [XX XX XX XX ...] 垃圾数据未初始化Widget* w new (raw_mem) Widget(42);这是placement new语法它做了以下事情不分配任何内存使用raw_mem指向的已有内存在raw_mem位置调用Widget的构造函数返回构造好的对象指针类型为Widget*内存状态变为raw_mem/w 指向的内存 [42] [其他成员变量...] 已初始化有效对象