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

  1. 分配内存:先用一个特殊函数,按 class 的定义分配了两个 double 的大小
  2. 转型(忽视)
  3. 调用构造函数,赋值(1,2)

7.3.2 delete

delete:先调用 dtor, 再释放 memory

image-20230731092947259

  1. 调用析构函数——释放的是 m_date 指向的字符串 Hello 的空间(即构造函数中 new 申请的空间)
  2. 释放内存:用一个特殊函数释放了 ps 指向的空间(即String* ps = new String("Hello");new 申请的空间)

7.4 内存动态分配

7.4.1 在VC下内存动态分配

在VC下(不同编译器的内存动态分配可能不同)

image-20230731095853726

  • 调试模式:

    (4*3) 是3个指针的大小

    (32+4) 是调试模式所需空间(橘色部分)

    (4*2) 是上下两个 cookie ——表示内存块的开始与结束

    4 是数组才有的长度记录

    由于分配内存块需要是16的倍数,所以需要 pad 来填充到64

  • 执行模式:

    去掉调试模式的空间即可

因为内存块是16的倍数,因此最后四位bit一定都是0,cookie 就借用最后的一位1表示占用内存,0表示释放内存

如上图41h1即表示占用内存

7.4.2 array new/delete

image-20230731101729210

==array new 一定要搭配 array delete==

==new后有[ ]—> delete后加[ ]==

普通的delete只调用一次析构函数——剩下两个指针的指向的空间没有调用析构函数,内存泄漏

这种情况发生在有指针的类,但最好都这样写