7 堆,栈,内存管理
7.1 堆和栈
Stack 栈,是存在于某作用域 (scope) 的一块内存空间。
例如当你调用函数,函数本身即会形成一个 stack
用来放置它所接收的参数,以及返回地址;在函数本体 (function body) 内声明的任何变量其所使用的内存块都取自上述 stack
Heap 堆,或称为 system heap ,是指由操作系统提供的一块 global 内存空间,程序可动态分配 (dynamic allocated) 从中获得若干区块 (blocks)
可以用 new
来动态取得
在 stack 中的是自动生成的空间,作用域结束空间会自动释放
在 heap 中的是自己申请的空间,需要自己释放
{
complex c1(1,2);
/*c1空间来自stack*/
complex* p = new complex(3);
/*complex(3) 是个临时对象
其所用的空间是以new从heap动态分配而得,并由p指向*/
}
7.2 object 生命期
-
stack objects 的生命期
c1
便是所谓 stack object,其生命在作用域 (scope) 结束之际结束这种作用域内的 object,又称为 auto object,因为它会被“自动”清理(结束自动调用析构函数){ complex c1(1,2); }
-
static local objects 的生命期
若在前面加上
static
后,其会存在到整个程序结束{ static complex c2(1,2); }
-
global objects 的生命期
写在任何作用域之外的对象,其生命在整个程序结束之后才结束,你也可以把它视为一种 static object,其作用域是整个程序
... complex c3(1,2); int main() { ... }
-
heap objects 的生命期
p
所指的便是 heap object,其生命在它被delete
之际结束{ complex* p = new complex; ... delete p; }
7.3 new 和delete
7.3.1 new
new:先分配 memory , 再调用 ctor
![image-20230731092332395](https://i-blog.csdnimg.cn/blog_migrate/fe142a883392259e72e72c5e46d47bb4.png)
- 分配内存:先用一个特殊函数,按 class 的定义分配了两个
double
的大小 - 转型(忽视)
- 调用构造函数,赋值
(1,2)
7.3.2 delete
delete:先调用 dtor, 再释放 memory
![image-20230731092947259](https://i-blog.csdnimg.cn/blog_migrate/6418c036e4c96b8c527cb647ada85615.png)
- 调用析构函数——释放的是
m_date
指向的字符串Hello
的空间(即构造函数中new
申请的空间) - 释放内存:用一个特殊函数释放了
ps
指向的空间(即String* ps = new String("Hello");
中new
申请的空间)
7.4 内存动态分配
7.4.1 在VC下内存动态分配
在VC下(不同编译器的内存动态分配可能不同)
![image-20230731095853726](https://i-blog.csdnimg.cn/blog_migrate/e9b3eec97ae8a35d35d7b771149d2e77.png)
-
调试模式:
(4*3)
是3个指针的大小(32+4)
是调试模式所需空间(橘色部分)(4*2)
是上下两个 cookie ——表示内存块的开始与结束4
是数组才有的长度记录由于分配内存块需要是16的倍数,所以需要 pad 来填充到
64
-
执行模式:
去掉调试模式的空间即可
因为内存块是16的倍数,因此最后四位bit一定都是0,cookie 就借用最后的一位
1
表示占用内存,0
表示释放内存如上图
41h
中1
即表示占用内存
7.4.2 array new/delete
array new
一定要搭配 array delete
new
后有[ ]
—> delete
后加[ ]
普通的delete只调用一次析构函数——剩下两个指针的指向的空间没有调用析构函数,内存泄漏
这种情况发生在有指针的类,但最好都这样写