一:程序内存
1.一个 C/C++ 程序的内存大致怎么分
程序内存可以分成了几个主要区域:栈、堆、数据段、代码段,其中,栈主要放非静态局部变量、函数参数等;堆负责运行时动态内存分配;数据段放全局和静态数据;代码段放可执行代码以及只读常量
2.指针变量在哪里,和它指向的东西在哪里
这是学内存管理必须真正想明白的一点。
例如:
cpp
int* ptr1 = (int*)malloc(sizeof(int) * 4);
这里实际上存在两个东西:
cpp
ptr1 本身
↓
一个局部指针变量
ptr1 指向的内存
↓
malloc 动态申请出来的一块空间
因为 ptr1 是 Test() 里的普通局部变量,所以:
ptr1 本身 → 栈
但是:
*ptr1 所在的动态内存 → 堆
同理:
cpp
int* p = new int;
也是:
p → 如果是局部变量,通常在栈上
*p → new 出来的空间在堆上
3.栈和堆的本质区别
栈上的内存通常随着函数调用自动管理。例如:
cpp
void func()
{
int a = 10;
}
a 是局部变量。
函数执行时它存在,函数结束之后,这部分局部对象的生命周期也随之结束。
而堆上的空间是程序运行过程中动态申请出来的,例如:
cpp
int* p = new int;
或者:
cpp
int* p = (int*)malloc(sizeof(int));
这块空间不会因为当前函数结束,就自动按照你写的动态内存接口帮你回收;所以强调动态申请的空间需要由自己配套释放
二:C 语言的动态内存管理:malloc / calloc / realloc / free
我们可以先简单回顾一下C 语言的动态内存管理:
cpp
malloc(size)
负责申请 size 字节空间。
例如:
cpp
int* p = (int*)malloc(sizeof(int) * 10);
意思是:申请能放10个int的空间
calloc 则常写成:
cpp
int* p = (int*)calloc(10, sizeof(int));
表示申请:
cpp
10 × sizeof(int)
大小的连续空间,并进行相应初始化处理。
realloc 是调整已有动态内存块的大小:
cpp
p = (int*)realloc(p, sizeof(int) * 20);
而:
cpp
free(p);
负责释放
三: C++ 的动态内存管理方式:new 和 delete
1.为什么 C++ 又发明了 new 和 delete?
C 的动态内存方式在 C++ 里仍然可以使用,但有些地方不够方便,因此 C++ 提供了自己的动态内存管理方式:
new和delete
C++ 有"对象"
一个对象不仅仅需要:一块大小合适的内存
它还可能需要:构造函数,析构函数
2.new / delete 操作内置类型
拿以下代码举例:
cpp
int* ptr4 = new int;
int* ptr5 = new int(10);
int* ptr6 = new int[3];
delete ptr4;
delete ptr5;
delete[] ptr6;
分别解释一下。
cpp
int* ptr4 = new int;
表示:动态申请一个 int 空间
而:
cpp
int* ptr5 = new int(10);
除了申请空间,还初始化:
cpp
*ptr5 = 10
再看:
cpp
int* ptr6 = new int[3];
这是一次创建三个连续的 int 空间。
所以释放的时候要注意:
delete ptr4;
delete ptr5;
delete[] ptr6;
特别强调:
单个元素使用
new/delete,连续多个元素使用new[]/delete[],必须匹配使用
这是必须记住的一条规则:
cpp
new ↔ delete
new[] ↔ delete[]
不要写:
cpp
int* p = new int[10];
delete p; // 不应该这样配
应该:
cpp
delete[] p;
3.new 自定义类型
以下定义了一个类:
cpp
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
然后比较:
cpp
A* p1 = (A*)malloc(sizeof(A));
A* p2 = new A(1);
最大的区别就是:
cpp
malloc
→ 只帮你得到一块内存
new
→ 得到内存
→ 还会调用构造函数
同样:
free
→ 释放内存
delete
→ 先调用析构函数
→ 再释放内存
申请自定义类型时,new 会调用构造函数,delete 会调用析构函数,而 malloc/free 不会
4.区分:"有内存" ≠ "已经是对象"
假设:
cpp
A* p = (A*)malloc(sizeof(A));
现在得到的是什么?
很多人可能会回答:一个 A 对象。
但是更准确地说只是:
一块足够容纳 A 的原始内存。
因为:
A()
构造函数还没执行。
所以你可以这样理解:
malloc(sizeof(A))
↓
┌─────────────┐
│ 一块原始空间 │
└─────────────┘
还没有经历 A 的构造过程
而:
A* p = new A;
则是:
申请空间
↓
调用 A()
↓
A 对象构造完成
这就是为什么 C++ 里的"对象生命周期"不能只用"内存有没有分配"来理解。
5.new 失败和 malloc 失败不同
cpp
malloc失败
→ 返回 NULL
new失败
→ 抛异常
所以传统 C 写法可能是:
cpp
int* p = (int*)malloc(100);
if (p == NULL)
{
...
}
而普通 C++ new 的错误处理机制不同
四:operator new与operator delete****函数
**1.**区分 new 和 operator new
假设有:
cpp
class A
{
public:
A(int x = 0)
: _x(x)
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _x;
};
我们写:
cpp
A* p = new A(10);
很多人会把这句话理解成:
cpp
申请 sizeof(A) 大小的内存
其实不完整。
对于自定义类型,new 原理是:
cpp
new A(10)
↓
operator new(sizeof(A))
申请原始内存
↓
调用 A 的构造函数
在这块内存上构造对象
↓
返回 A*
自定义类型执行 new 时,会先调用 operator new 申请空间,再在申请出的空间上执行构造函数。
所以:
cpp
new A(10)
可以从概念上理解成:
cpp
void* ptr = operator new(sizeof(A));
// 在这块已经申请好的空间上构造 A 对象
A* p = new(ptr) A(10);
2.operator new 到底是什么?
operator new 是一个函数。
可以理解为:
cpp
void* operator new(size_t size);
参数:
cpp
size
表示:我要申请多少字节
返回:void*
表示:返回申请到的原始内存地址。
例如:
cpp
void* p = operator new(100);
意思就是:给我申请 100 字节的原始内存。
但是注意:
cpp
operator new(100);
并不知道你最终要在这里放:
int
Date
A
String
中的哪一种对象。
它的核心任务就是:
负责空间,不负责对象构造。
所以 operator new 不会调用构造函数
例如:
cpp
A* p = (A*)operator new(sizeof(A));
现在虽然 p 指向一块足够放下 A 的内存,但是:
cpp
A()
还没有被调用,所以
cpp
有了一块 sizeof(A) 的内存
≠
A 对象已经完成构造
如果想真正把 A 构造起来,可以继续:
cpp
new(p) A(10);
于是:
cpp
operator new(sizeof(A))
↓
只有原始空间
new(p) A(10)
↓
调用构造函数
↓
A 对象构造完成
3.operator delete 又是什么?
同理:
cpp
operator delete
也是一个函数,主要负责:
释放原始内存。
可以理解成:
cpp
void operator delete(void* p);
例如:
cpp
operator delete(p);
负责释放 p 指向的空间。
但是:
operator delete本身不是delete操作符,它的核心工作不是替你调用对象析构函数。
4.delete 和 operator delete 又是什么关系?
假设:
cpp
A* p = new A(10);
最后:
cpp
delete p;
delete 原理是:
cpp
delete p
↓
调用析构函数 ~A()
清理对象内部资源
↓
调用 operator delete
释放对象占据的原始空间
也就是说:
cpp
delete p;
概念上可以拆成:
p->~A();
operator delete(p);
delete是先执行析构函数完成资源清理,再调用 operator delete 释放空间。