C++从入门到实战(十二)详细讲解C++如何实现内存管理
- 前言
- 一、C++内存管理方式
-
- [1. new/delete操作内置类型](#1. new/delete操作内置类型)
- [2. 异常与内存管理的联系(简单了解)](#2. 异常与内存管理的联系(简单了解))
- [3. new和delete操作自定义类型](#3. new和delete操作自定义类型)
- [二、 operator new与operator delete函数(重点)](#二、 operator new与operator delete函数(重点))
-
- [1. new和delete操作符与operator new和operator delete函数的关系](#1. new和delete操作符与operator new和operator delete函数的关系)
- [2. operator new函数的工作原理](#2. operator new函数的工作原理)
- [3. operator delete函数的工作原理](#3. operator delete函数的工作原理)
- [三、 定位new表达式(placement-new) (了解即可)](#三、 定位new表达式(placement-new) (了解即可))
-
- [1. 定位 new 表达式的概念](#1. 定位 new 表达式的概念)
- [2. 定位 new 表达式的使用格式](#2. 定位 new 表达式的使用格式)
- [3. 定位 new 表达式的使用场景](#3. 定位 new 表达式的使用场景)
- 四、malloc/free和new/delete的区别
-
- [1. 相同点](#1. 相同点)
- [2. 不同点](#2. 不同点)
-
- 一个是函数,一个是操作符
- [new 会 "初始化",malloc 不会](#new 会 “初始化”,malloc 不会)
- [空间大小:new 自动计算,malloc 要手动算](#空间大小:new 自动计算,malloc 要手动算)
- [new 不用强转,malloc 需要](#new 不用强转,malloc 需要)
- [错误处理:malloc 返 NULL,new 抛异常](#错误处理:malloc 返 NULL,new 抛异常)
- [自定义类型:new/delete 会 "照顾" 对象,malloc/free 不会](#自定义类型:new/delete 会 “照顾” 对象,malloc/free 不会)
前言
- 在上一篇博客中,我们探讨了 C/C++ 语言的内存分布模型,并对比了 C 与 C++ 内存管理的核心差异,初步认识了 C++ 中new与delete的基本概念,为理解 C++ 内存管理体系奠定了基础。
- 本文将在此基础上,深入解析 C++ 内存管理的核心机制,从底层原理到实践细节展开系统讲解,帮助读者全面掌握这一重要知识模块。
我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343我的C++知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12880513.html?spm=1001.2014.3001.5482
一、C++内存管理方式
1. new/delete操作内置类型
- 在 C 语言里有它自己的内存管理办法,但用起来比较麻烦,而且有些情况处理不了。
- C++ 为了解决这些问题,引入了new和delete操作符来进行动态内存管理。
cpp
#include <iostream>
using namespace std;
void Test()
{
int* ptr4 = new int;
// 动态申请一个int类型的空间
int* ptr5 = new int(10);
//动态申请一个int类型的空间并初始化为10
int* ptr6 = new int[3];
//动态申请3个int类型的空间
delete ptr4;
delete ptr5;
//delete ptr6;//错误在 C++ 中,new 和 delete、new[] 和 delete[] 是成对出现的操作符,它们的使用需要遵循特定的配对规则:
//如果使用 delete 而非 delete[] 来释放通过 new[] 分配的数组内存,会产生严重的后果:
//内存泄漏:delete 只会释放数组首元素的内存,而不会释放数组中其他元素的内存,这就导致了部分内存无法被回收,造成内存泄漏。
//未定义行为:使用 delete 释放数组内存属于未定义行为,这意味着程序可能会出现各种不可预测的问题,比如程序崩溃、数据损坏等
delete[] ptr6;
}
- 在 C++ 中,new 和 delete、new[] 和 delete[] 是成对出现的操作符,它们的使用需要遵循特定的配对规则:
- 如果使用 delete 而非 delete[] 来释放通过 new[] 分配的数组内存,会产生严重的后果 :例如上面代码中的
int* ptr6 = new int[3]与delete ptr6
内存泄漏 :delete 只会释放数组首元素的内存,而不会释放数组中其他元素的内存,这就导致了部分内存无法被回收,造成内存泄漏。
未定义行为:使用 delete 释放数组内存属于未定义行为,这意味着程序可能会出现各种不可预测的问题,比如程序崩溃、数据损坏等

2. 异常与内存管理的联系(简单了解)
- 在使用new申请内存时,如果系统没有足够的内存可供分配,就可能会抛出异常。
- 所以在进行内存管理时,要考虑到这种可能出现的异常情况,确保程序的健壮性。
cpp
#include <iostream>
using namespace std;
void Test() {
try {
// 动态申请一个 int 类型的空间
int* ptr4 = new int;
// 动态申请一个 int 类型的空间并初始化为 10
int* ptr5 = new int(10);
// 动态申请 3 个 int 类型的空间
int* ptr6 = new int[3];
// 使用分配的内存
*ptr4 = 5;
cout << "Value of ptr4: " << *ptr4 << endl;
cout << "Value of ptr5: " << *ptr5 << endl;
for (int i = 0; i < 3; ++i) {
ptr6[i] = i;
cout << "Value of ptr6[" << i << "]: " << ptr6[i] << endl;
}
// 释放内存
delete ptr4;
delete ptr5;
delete[] ptr6;
} catch (const bad_alloc& e) {
// 捕获内存分配失败的异常
cerr << "Memory allocation failed: " << e.what() << endl;
}
}
int main() {
Test();
return 0;
}

cpp
#include <iostream>
using namespace std;
void Func()
{
int i = 1;
while (1)
{
int* p1 = new int[1024 * 1024];
cout << i << "->" << p1 << endl;
i++;
}
}
int main()
{
try
{
Func();
}
catch (const std::exception& e)
{
cout << e.what() << endl;
}
}

- 这段代码定义了一个名为 Func 的函数,该函数在一个无限循环中不断尝试分配大约1MB的内存。
- 由于没有适当的内存释放,这将最终导致内存耗尽,从而抛出 std::bad_alloc 异常。
- 在 main 函数中, Func 被调用并被包裹在一个 try-catch 块中,以捕获并处理可能抛出的异常。
然而,由于 std::bad_alloc 异常没有被直接捕获( catch 块中捕获的是 std::exception 的引用),所以实际上这段代码可能不会按预期工作,因为 std::bad_alloc 可能在到达 catch 块之前就已经导致程序终止
3. new和delete操作自定义类型
- 在 C++ 里,new 和 delete 除了能操作基本数据类型,还可以操作自定义类型。
自定义类型是程序员自己定义的类型,像类、结构体等.
- 以下是new 和 delete 操作基本数据类型的简单代码
cpp
#include <iostream>
using namespace std;
class Person
{
public:
Person(const char* _name,int _age)
{
name = _name;
age = _age;
cout << "Person 构造函数被调用,名字: " << name << ", 年龄: " << age;
};
~Person()
{
cout << "Person 析构函数被调用: " << name << ", 年龄: " << age;
}
private:
const char * name;//一个指向常量字符的指针类型
int age;
};
int main() {
// 使用 new 创建 Person 对象
Person* person = new Person("Alice", 25);
// 使用 delete 释放对象内存
delete person;
return 0;
}

这段代码定义了一个 Person 类,包含姓名和年龄两个属性,还有构造函数和析构函数。在 main 函数里,使用 new 操作符创建一个 Person 对象,然后使用 delete 操作符释放对象的内存。
二、 operator new与operator delete函数(重点)
1. new和delete操作符与operator new和operator delete函数的关系
- 前面我们讲到new和delete是用来动态申请和释放内存的操作符。比如,你想创建一个int类型的变量,就可以用new操作符来申请内存:
cpp
int* ptr = new int;
-
这里的new操作符在底层会调用operator new全局函数来申请内存空间
-
当你不再需要这块内存时,就得用delete操作符释放它:
cpp
delete ptr;
delete操作符在底层会调用operator delete全局函数来释放内存空间
2. operator new函数的工作原理
- operator new函数的作用是申请内存空间,它实际上是借助malloc函数来实现的。
下面是它的工作步骤:
- 尝试申请内存:调用malloc函数去申请指定大小的内存空间。
- 检查申请结果:
- 成功:若malloc申请内存成功,就直接返回这块内存的指针。
- 失败:若malloc申请内存失败,会尝试执行用户设置的空间不足应对措施。
- 用户设置了应对措施:继续尝试申请内存。
- 用户未设置应对措施:抛出std::bad_alloc类型的异常
下面是operator new函数的简化代码:
cpp
void *operator new(size_t size) {
void *p;
while ((p = malloc(size)) == 0) {
if (用户设置的应对措施函数(size) == 0) {
// 申请内存失败,抛出异常
static const std::bad_alloc nomem;
throw nomem;
}
}
return p;
}
3. operator delete函数的工作原理
- operator delete函数的作用是释放内存空间,它最终是通过free函数来实现的。
下面是它的工作步骤:
- 检查指针是否为空:若传入的指针为空,直接返回,不做任何操作。
- 释放内存:调用free函数释放这块内存空间。
下面是operator delete函数的简化代码:
cpp
void operator delete(void *pUserData) {
if (pUserData == NULL)
return;
free(pUserData);
}
- new操作符在底层调用operator new函数来申请内存空间,operator new函数又借助malloc函数来申请内存。
- delete操作符在底层调用operator delete函数来释放内存空间,operator delete函数最终通过free函数来释放内存。
- 若malloc申请内存失败,operator new函数会尝试执行用户设置的应对措施,若没有设置则抛异常
三、 定位new表达式(placement-new) (了解即可)
1. 定位 new 表达式的概念
一般而言,使用new操作符时,它会做两件事:
- 一是为对象分配内存;二是调用对象的构造函数来初始化这块内存。
- 而定位 new 表达式有所不同,它是在已经分配好的原始内存空间里调用构造函数来初始化对象。
2. 定位 new 表达式的使用格式
在C++中,定位 new 的语法如下
cpp
new (address) Type
在address指向的内存空间创建一个type类型的对象
cpp
new (address) Type[size]
-
在address指向的内存空间创建一个type类型的对象,并且用size里的值来初始化对象。
-
这里的address得是一个指针,size是类型的初始化列表
3. 定位 new 表达式的使用场景
- 在实际运用中,定位 new 表达式通常和内存池配合使用。
- 内存池分配的内存并未初始化,要是分配的是自定义类型的对象,就得使用定位 new 表达式来显式调用构造函数进行初始化
cpp
#include <iostream>
int main() {
// 分配一块原始内存
char* rawMemory = new char[sizeof(int)];
// 使用定位new在原始内存上构造一个int对象
int* intPtr = new (rawMemory) int(42);
// 输出构造的int对象的值
std::cout << "Value of int object: " << *intPtr << std::endl;
// 显式调用析构函数(对于基本类型,这一步不是必需的,但对于自定义类型是必需的)
intPtr->~int();
// 释放原始内存
delete[] rawMemory;
return 0;
}
cpp
#include <iostream>
class MyClass {
public:
MyClass(int value) : data(value) {
std::cout << "Constructor called with value: " << data << std::endl;
}
~MyClass() {
std::cout << "Destructor called for value: " << data << std::endl;
}
private:
int data;
};
int main() {
// 分配一块原始内存
char* rawMemory = new char[sizeof(MyClass)];
// 使用定位new在原始内存上构造一个MyClass对象
MyClass* myObjPtr = new (rawMemory) MyClass(10);
// 显式调用析构函数
myObjPtr->~MyClass();
// 释放原始内存
delete[] rawMemory;
return 0;
}
四、malloc/free和new/delete的区别
1. 相同点
malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。
- 不过,他们的不同点是
2. 不同点
一个是函数,一个是操作符
- malloc/free 是 C 语言的库函数:需要包含头文件 <stdlib.h>,用函数的方式调用(比如 malloc(size))。
- new/delete 是 C++ 的操作符:是 C++ 语言内置的功能,用法更简洁(比如 new int)
new 会 "初始化",malloc 不会
malloc 申请的内存是 "脏的":里面可能是随机的垃圾值。比如:
cpp
int* p = (int*)malloc(sizeof(int)); // *p 的值不确定,可能是任意数
- new 申请的内存会被 "初始化" :
- 对内置类型(如 int、double),new int 不会初始化,但 new int() 会初始化为 0;
- 对自定义类型(如类),new 会自动调用构造函数,完成对象的初始化(比如给成员变量赋值)。
空间大小:new 自动计算,malloc 要手动算
- malloc 需要自己算大小:必须用 sizeof 计算需要的字节数,比如申请 5 个 int 的空间:
cpp
int* p = (int*)malloc(5 * sizeof(int)); // 手动算 5*4=20 字节
- 如果算错(比如漏掉 sizeof 或乘错数),就会出 bug
- new 自动知道要多大:直接写类型和数量即可,比如:
cpp
int* p = new int[5]; // 自动申请 5 个 int 的空间,不用算字节数
new 不用强转,malloc 需要
malloc 返回 void* 指针:使用时必须强制转换类型,比如:
cpp
int* p = (int*)malloc(sizeof(int)); // 必须强转成 int*
new 直接返回对应类型的指针:比如 new int 直接返回 int*,不需要强转:
cpp
int* p = new int; // 直接是 int* 类型,不用强转
错误处理:malloc 返 NULL,new 抛异常
- malloc 申请失败返回 NULL:必须检查是否为 NULL,否则解引用(比如 *p)会导致程序崩溃:
cpp
int* p = (int*)malloc(sizeof(int));
if (p == NULL) { // 必须判空!
// 处理内存不足的情况
}
- new 申请失败会抛出异常:默认会抛出 std::bad_alloc 异常,需要用 try-catch 捕获(或者用 new(nothrow) 版本返回 NULL,但不常用):
cpp
try {
int* p = new int; // 失败会抛异常,不会返回 NULL
} catch (std::bad_alloc& e) {
// 处理异常
}
自定义类型:new/delete 会 "照顾" 对象,malloc/free 不会
malloc/free 只负责搬砖:
- malloc 只会分配一块足够大的内存,但不会调用类的 构造函数(比如初始化成员变量);
- free 只会释放内存,但不会调用类的 析构函数(比如释放对象内部申请的资源)。
这样会导致对象没被正确初始化或清理,造成错误或内存泄漏。
new/delete 会 "盖房子" 和 "拆房子":
- new 分配内存后,会自动调用类的构造函数,初始化对象(比如给成员变量赋值);
- delete 释放内存前,会自动调用类的析构函数,清理对象内部的资源(比如释放成员指针指向的内存)。
以上就是这篇博客的全部内容,下一篇我们将继续探索C++中模板初阶更多精彩内容。
我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343我的C++知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12880513.html?spm=1001.2014.3001.5482
|--------------------|
| 非常感谢您的阅读,喜欢的话记得三连哦 |
