引言
在C语言的学习中,我们已经初步了解了C语言的内存分布,这篇文章将介绍C++相关的内存管理内容
1.C/C++的内存分布
- 1.1 题目
在正式开始讲解内存管理前,我们先做一个小测试,检测一下之前的知识还剩了多少()

答案(点击箭头查看)
CCCAA AAADAB
因为大部分的都比较简单,这里就挑几个较难的讲解:
1.pchar3 *pchar3
因为pchar3是一个指针,所以处在栈上,但*pchar3是首元素,首元素‘a’处于常量区,因此*pchar3在常量区
2.ptr1 *ptr1
同样的,ptr1也是一个指针,处于栈上,但ptr1的空间是动态开辟的,因此*ptr1指向的是堆
为了防止还不够清楚,这里再给一张图解释

- 1.2 各个区域的分布情况
栈:局部变量
堆:新开的,动态申请的数据(malloc,realloc等)
数据段(静态区):全局变量,静态变量
代码段(常量区):可执行的代码,常量
这里给一张图可以更好的理解:

2.C语言中内存的管理方式
- 2.1 类型
在C语言中,我们常采用malloc,colloc,realloc来进行内存管理
- 2.2 区别
1.malloc
功能为分配指定字节数的未初始化内存块,失败返回NULL,分配的内容为随机值
用法:int *arr = (int *)malloc(10 * sizeof(int)); // 分配10个int大小的空间
2.calloc
分配指定数量、指定大小的内存块,失败返回NULL,并将内容初始化为0
用法:int *arr = (int *)calloc(10, sizeof(int)); // 分配10个int,全部初始化为0
3.realloc
重新调整已分配内存块的大小,失败返回NULL,前提是在realloc前对象已被初始化,否则将失败
用法:int *new_arr = (int *)realloc(arr, 20 * sizeof(int)); // 扩大到20个int,arr是已存在的对象
3.C++管理内存方式
- 3.1 new和delete的用法
1.基本用法
int* p1 = new int;
int* p2 = new int[10];
delete p1;
delete[] p2;
要注意的是如果开辟了>=1个空间,delete时要加上[]
2.原始类型初始化用法
int* p1 = new int(0);
int* p2 = new int[5]{1, 2, 3, 4, 5};
delete p1;
delete[] p2;
对于原始类型的初始化,只有一个空间的在类型后接()即可,对于多个空间的,在类型后接{},在括号内填上初始化的数据,没有初始化的位置默认初始化为0
3.自定义类型初始化用法
class A
{
public:
A(int a, int b): _a(a), _b(b)
{}
private:
int _a;
int _b;
}
A* p1 = new A({1, 2});
A* p2 = new A[5]{{1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6}};
对于自定义类型也可以进行初始化,但是要求自定义类型要有相对应的构造函数,如果初始化多个数据,要在{}内再加{}进行初始化,其实也就是用到了匿名对象和隐式类型转化的原理(详细见这篇文章:类和对象(下) – 小园得志)具体用法如上所示
- 3.2 抛异常(new失败)
1.核心概念
抛异常处理是 C++ 用于处理运行时错误的机制,让程序在出错时能够优雅地恢复或退出,而不是直接崩溃
2.执行流程
程序正常执行 → 遇到 throw → 跳转到匹配的 catch → 处理异常 → 继续执行 catch 之后的代码
3.解释+例子
接下来我将结合这个例子,为大家解释抛异常
void deep_function() {
std::cout << "我要申请超大内存..." << std::endl;
// 申请 1TB 内存,必然失败
int* p = new int[1000000000000];
// ❗ 如果 new 失败,这里不会执行!直接跳到 catch
std::cout << "申请成功,不会打印这行" << std::endl;
*p = 10;
}
void middle_function() {
std::cout << "中间层:准备调用 deep_function" << std::endl;
deep_function(); // 异常从这里抛出来,middle_function 也不处理
std::cout << "中间层:调用完成,不会打印这行" << std::endl;
}
int main() {
try {
std::cout << "main:开始" << std::endl;
middle_function(); // 异常一路抛到 main
std::cout << "main:调用完成,不会打印这行" << std::endl;
}
catch (std::bad_alloc& e) { // ✅ 在这里捕获!
std::cout << "main:捕获到异常!原因:" << e.what() << std::endl;
}
std::cout << "main:程序继续运行,没有崩溃" << std::endl;
return 0;
}
try内是需要检测的程序,如果try内的代码能够正常运行,那么就会直接跳过catch内的代码继续执行下面的代码,如果try内代码不能够正常运行,则会直接执行catch内的代码,然后执行catch后的代码,对于上述代码来说,内存分配过大一定会失败,当执行到内存分配的那一行代码时,就会直接跳转到catch(try内的剩余代码也将不执行),执行catch内的代码,然后继续执行剩余代码
此代码运行结果如下:

- 3.3 new和delete的底层原理
1.原始类型
如果new和delete的对象是原始类型,那么new的底层就是malloc,delete的底层就是free
2.自定义类型
对于自定义类型来说,new的底层就是利用malloc开空间,然后调用自定义对应的构造函数初始化,delete的底层则是先调用析构函数,再用free清理空间
总结
本来以为内存管理的内容写不了多少的,但是在写的过程中发现有很多没有掌握完全的知识点,就越写越多了,也算是自我提升了吧

评论(0)
暂无评论