0


【C++】—— 内存管理

【C++】—— 内存管理

1 C/C++ 的内存划分

1.1 C/C++ 的内存分布

  一个程序的数据存储是需要分区的,C/C++ 中常见的区域有:

静态区(数据段)

常量区(代码段)

在这里插入图片描述

说明:

  • :又叫堆栈,非静态局部变量/函数参数/返回值等,栈是向下增长
  • 内存映射段:是高效的 I/O 映射方式,用于转载一个共享的动态内存库,用户可使用系统接口创建共享内存,做进程通信。
  • :用于程序运行时动态内存分配,堆是向上增长
  • 数据段:存储全局数据静态数据
  • 代码段:可执行的代码/只读常量

1.2 C/C++ 的内存分布练习

我们来看下面一段代码及相关问题:

int globalVar =1;staticint staticGlobalVar =1;voidTest(){staticint staticVar =1;int localVar =1;int num1[10]={1,2,3,4};char char2[]="abcd";constchar* pChar3 ="abcd";int* ptr1 =(int*)malloc(sizeof(int)*4);int* ptr2 =(int*)calloc(4,sizeof(int));int* ptr3 =(int*)realloc(ptr2,sizeof(int)*4);free(ptr1);free(ptr3);}

题目:

  • 选项:A.栈   B.堆   C.数据段(静态区)   D.代码段(常量区)
  1.                                               g                                  l                                  o                                  b                                  a                                  l                                  V                                  a                                  r                                          globalVar                           globalVar 在哪里?______C
    
  2.                                               s                                  t                                  a                                  t                                  i                                  c                                  G                                  l                                  o                                  b                                  a                                  l                                  V                                  a                                  r                                          staticGlobalVar                           staticGlobalVar 在哪里?______C
    
  3.                                               s                                  t                                  a                                  t                                  i                                  c                                  V                                  a                                  r                                          staticVar                           staticVar 在哪里?______C
    
  4.                                               l                                  o                                  c                                  a                                  l                                  V                                  a                                  r                                          localVar                           localVar 在哪里?______A
    
  5.                                               n                                  u                                  m                                  1                                          num1                           num1 在哪里?______A
    
  6.                                               c                                  h                                  a                                  r                                  2                                          char2                           char2 在哪里?______A
    
  7.                                               ∗                                  c                                  h                                  a                                  r                                  2                                          *char2                           ∗char2 在哪里?______A
    
  8.                                               p                                  C                                  h                                  a                                  r                                  3                                          pChar3                           pChar3 在哪里?______A
    
  9.                                               ∗                                  p                                  C                                  h                                  a                                  r                                  3                                          *pChar3                           ∗pChar3 在哪里?______D
    
  10.                                               p                                  t                                  r                                  1                                          ptr1                           ptr1 在哪里?______A
    
  11.                                               ∗                                  p                                  t                                  r                                  1                                          *ptr1                           ∗ptr1 在哪里?______B
    

答案详解:

  1. globalVar全局变量,全局变量放在静态区。  C
  2. staticGlobalVar 也是全局静态变量,放在静态区。与 g l o b a l V a r globalVar globalVar 的区别是: s t a t i c G l o b a l V a r staticGlobalVar staticGlobalVar 被 s t a t i c static static 修饰,只能在当前文件使用。 C
  3. staticVar局部静态变量,局部静态变量虽然访问作用域在函数中,但它与全局变量一样,存放在静态区。  C
  4. localVar局部变量,局部变量放在栈****A
  5. num1数组名,数组名表示的是首元素的地址,也可以表示整个数组。这里是后者,即开辟出的10个 i n t int int 空间,静态数组开辟的空间都是在栈数开辟的。  A
  6. char2 与上述 n u m 1 num1 num1 类似,表示的是开辟出的 5 个 c h a r char char 空间(别忘了‘\0’)。  A
  7. *char2 这里表示的是数组首元素的地址,解引用即表示第一个元素 ‘ a a a’。虽然我们知道常量字符串是存储在常量区,但是,数组中存的 “ a b c d abcd abcd” 是从常量区中 拷贝 过来存储在数组中,依然是在上。  A
  8. pChar3指针变量,是局部变量,是在栈上开辟 4/8 个空间存储 “ a b c d abcd abcd” 首字符的地址的变量。  A
  9. *pChar3 指向 “ a b c d abcd abcd” 首元素的地址 ‘ a a a’,解引用即问 ‘ a a a’ 存储在哪。“ a b c d abcd abcd” 是只读常量,放在常量区。  D
  10. ptr1 与 p C h a r 3 pChar3 pChar3 同理,都是存储在上。  A
  11. ptr1 指向动态开辟的空间,而 *ptr1 则是问动态开辟出的空间存储在哪,存储在堆上。  B

在这里插入图片描述

 某种程度来说,分区分的是什么呢?分的是生命周期!不同的对象,声明周期是不一样的
 我需要一次性的,就是

局部变量

,我需要长期使用的就是

静态

的,我需要动态申请释放的,就是

上的,我需要不修改的,就是

常量区

 我们程序员主要关注的就是

堆区

,只有堆区是交给程序员自己控制的,其他都是编译器在管

2 C语言 中动态内存管理方式:malloc/calloc/realloc/free

  关于上述 4 个函数,在 C语言 的学习中已详细讲解,这里就不再赘述,感兴趣的小伙伴可跳转至此:【C语言】—— 动态内存管理

3 C++ 内存管理方式

3.1 new / delete 操作内置类型

  C语言 内存管理方式在 C++ 中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此 C++ 又提出了自己的内存管理方式:通过

       n 
      
     
       e 
      
     
       w 
      
     
    
      new 
     
    
  new 和  
   
    
     
     
       d 
      
     
       e 
      
     
       l 
      
     
       e 
      
     
       t 
      
     
       e 
      
     
    
      delete 
     
    
  delete 操作符进行动态内存管理。
intmain(){//动态申请一个int类型空间int* p1 =newint;//动态申请10个int类型空间int* p2 =newint[10];//动态申请一个int类型空间,并初始化为10int* p3 =newint(10);//动态申请10个int类型空间,并将前面3个初始化为1,后面默认初始化为0int* p4 =newint[5]{1,1,1,};//释放空间//new匹配delete,new[]匹配delete[]delete p1;delete[] p2;delete p3;delete[] p4;return0;}

在这里插入图片描述

  可以看到,与

     m 
    
   
     a 
    
   
     l 
    
   
     l 
    
   
     o 
    
   
     c 
    
   
  
    malloc 
   
  
malloc 不同, 
  
   
    
    
      n 
     
    
      e 
     
    
      w 
     
    
   
     new 
    
   
 new 是支持初始化的。

  未给值时,默认不初始化。给了值则初始化,只给了部分值,后面默认初始化为0

在这里插入图片描述

3.2 new 和 delete 操作自定义类型

3.2.1 new 和 delete 操作自定义类型基础

  C++ 抛弃

     m 
    
   
     a 
    
   
     l 
    
   
     l 
    
   
     o 
    
   
     c 
    
   
  
    malloc 
   
  
malloc 而自己创建  
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 和  
 
  
   
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete 仅仅是为了上述能够初始化吗?肯定不是的,谜底出现在**自定义类型**上
classA{public:A(int a =0):_a(a){
        cout <<"A(int a)"<< endl;}~A(){
        cout <<"~A()"<< endl;}voidPrint(){
        cout <<"_a = "<< _a << endl;}private:int _a =0;};intmain(){
    A* p1 =new A;
    A* p2 =newA(10);

    p1->Print();
    p2->Print();delete p1;delete p2;return0;}

运行结果:

在这里插入图片描述

     n 
    
   
     e 
    
   
     w 
    
   
     / 
    
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    new/delete 
   
  
new/delete 和  
 
  
   
   
     m 
    
   
     a 
    
   
     l 
    
   
     l 
    
   
     o 
    
   
     c 
    
   
     / 
    
   
     f 
    
   
     r 
    
   
     e 
    
   
     e 
    
   
  
    malloc/free 
   
  
malloc/free
最大区别

是 :

      n 
     
    
      e 
     
    
      w 
     
    
      / 
     
    
      d 
     
    
      e 
     
    
      l 
     
    
      e 
     
    
      t 
     
    
      e 
     
    
   
     new/delete 
    
   
 new/delete 对于自定义类型除了开空间还会自动调用其构造函数和析构函数

3.2.2 自动调用构造和析构的好处

  那有小伙伴问:自动调用构造和析构函数真的那么重要吗?

我们以链表节点来感受一下:

structListNode{int val;
    ListNode* next;ListNode(int x =0):val(x),next(nullptr){}};

  有做过 C++ 相关 OJ 的小伙伴都知道,C++ 中链表是这样给的

  这时,我们初始化节点就很方便啦

intmain(){
    ListNode* p1 =new ListNode;
    ListNode* p2 =newListNode(1);
    ListNode* p3 =newListNode(2);
    p1->next = p2;
    p2->next = p3;delete p1;delete p2;delete p3;return0;}

  想一下,如果是 C语言 是怎么实现的?是不是还要自己写一个创建节点的函数,而销毁节点是不是也要自己写一个函数。现在好了

      n 
     
    
      e 
     
    
      w 
     
    
   
     new 
    
   
 new 和  
  
   
    
    
      d 
     
    
      e 
     
    
      l 
     
    
      e 
     
    
      t 
     
    
      e 
     
    
   
     delete 
    
   
 delete**自动调用构造和析构**,活都帮你干了。

3.2.3 多参数自定义类型

  对于多参数的自定义类型,

     n 
    
   
     e 
    
   
     w 
    
   
     / 
    
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    new/delete 
   
  
new/delete 也同样会调用其构造与析构函数,但如何传参呢?
classA{public:A(int a =0,int b =0):_a(a),_b(b){
        cout <<"A(int a)"<< endl;}A(const A& a){
        _a = a._a;
        _b = a._b;
        cout <<"A(const A& a)"<< endl;}~A(){
        cout <<"~A()"<< endl;}voidPrint(){
        cout <<"_a = "<< _a << endl;}private:int _a =0;int _b =0;};intmain(){
    A* p1 =newA(1,1);
    cout << endl;//数组多参数给值方法//法一:严格来说这不是调用构造,而是拷贝构造
    A a1(1,1);
    A a2(2,2);
    A a3(3,3);
    A* p2 =new A[3]{ a1,a2,a3 };
    cout << endl;//法二:匿名对象(编译器优化成直接构造)
    A* p3 =new A[3]{A(1,1),A(2,2),A(3,3)};
    cout << endl;//法三:隐式类型转换(编译器优化成直接构造)
    A* p4 =new A[3]{{1,1},{2,2},{3,3}};
    cout << endl;return0;}

运行结果:

在这里插入图片描述

在这里插入图片描述

3.2.4 不存在默认构造

  但如果自定义类型不存在默认构造就需要注意啦

classA{public:A(int a):_a(a){
        cout <<"A(int a)"<< endl;}~A(){
        cout <<"~A()"<< endl;}voidPrint(){
        cout <<"_a = "<< _a << endl;}private:int _a =0;};intmain(){
    A* p =new A;return0;}

在这里插入图片描述

  如果自定义类型不存在默认构造函数,那么编译器会报错

  一般情况下,我们不再使用

     m 
    
   
     a 
    
   
     l 
    
   
     l 
    
   
     o 
    
   
     c 
    
   
     / 
    
   
     f 
    
   
     r 
    
   
     e 
    
   
     e 
    
   
  
    malloc/free 
   
  
malloc/free,因为  
  
   
    
    
      n 
     
    
      e 
     
    
      w 
     
    
      / 
     
    
      d 
     
    
      e 
     
    
      l 
     
    
      e 
     
    
      t 
     
    
      e 
     
    
   
     new/delete 
    
   
 new/delete 兼容了他们所有的用法,还能自动调用构造和析构

3.2.5 空间开辟失败

  用

     m 
    
   
     a 
    
   
     l 
    
   
     l 
    
   
     o 
    
   
     c 
    
   
  
    malloc 
   
  
malloc 开辟空间,要**对返回值进行检查**,检查返回值是否为
空指针

。那用

     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 开辟空间要不要检查呢?

  也是

的,

       n 
      
     
       e 
      
     
       w 
      
     
    
      new 
     
    
  new 失败是 **抛异常**,这里我们简单提一下,更多知识将会后面学习

注:一般情况下,空间开辟不会失败,一般都是空间开的太大才会失败

  抛一共有个关键字:

      t 
     
    
      h 
     
    
      r 
     
    
      o 
     
    
      w 
     
    
   
     throw 
    
   
 throw 
  
   
    
    
      t 
     
    
      r 
     
    
      y 
     
    
   
     try 
    
   
 try 
  
   
    
    
      c 
     
    
      a 
     
    
      t 
     
    
      c 
     
    
      h 
     
    
   
     catch 
    
   
 catch

  当发生异常,

      t 
     
    
      h 
     
    
      r 
     
    
      o 
     
    
      w 
     
    
   
     throw 
    
   
 throw 抛出一个对象,对异常进行处理要引入  
  
   
    
    
      t 
     
    
      r 
     
    
      y 
     
    
   
     try 
    
   
 try(尝试) /  
  
   
    
    
      c 
     
    
      a 
     
    
      t 
     
    
      c 
     
    
      h 
     
    
   
     catch 
    
   
 catch(捕获)

这里我们申请 2G 的空间试试:

intmain(){void* p1 =newchar[1024*1024*1024];
    cout << p1 << endl;void* p2 =newchar[1024*1024*1024];
        cout << p2 << endl;return0;}

注:

     1 
    
   
     G 
    
   
     = 
    
   
     1024 
    
   
     M 
    
   
     ; 
    
   
     1 
    
   
     M 
    
   
     = 
    
   
     1024 
    
   
     K 
    
   
     ; 
    
   
     1 
    
   
     K 
    
   
     = 
    
   
     1024 
    
   
     B 
    
   
     y 
    
   
     t 
    
   
     e 
    
   
     ; 
    
   
     1 
    
   
     B 
    
   
     y 
    
   
     t 
    
   
     e 
    
   
     = 
    
   
     8 
    
   
     b 
    
   
     i 
    
   
     t 
    
   
  
    1G = 1024M ;1M = 1024K; 1K = 1024Byte ;1Byte = 8bit 
   
  
1G=1024M;1M=1024K;1K=1024Byte;1Byte=8bit

运行结果:

在这里插入图片描述

  申请 第一G 还是没问题的,申请 第二G 程序中断

  我们对其进行捕获(先不用管怎么写,知道这是在捕获就行)

intmain(){try{void* p1 =newchar[1024*1024*1024];void* p2 =newchar[1024*1024*1024];}catch(const exception& e){
        cout << e.what()<< endl;}return0;}

运行结果:

在这里插入图片描述

  打印:

      b 
     
    
      a 
     
    
      d 
     
    
      a 
     
    
      l 
     
    
      l 
     
    
      o 
     
    
      c 
     
    
      a 
     
    
      t 
     
    
      i 
     
    
      o 
     
    
      n 
     
    
   
     bad allocation 
    
   
 badallocation,表示
已经没有你需要的那么大的内存了

  我们还可以来看一下在 32 位平台下可以申请到多大空间:

voidfunc(){int n =0;while(1){
        n++;void* p =newchar[1024*1024];//一次向堆申请一字节的内存
        cout << p <<":->"<< n << endl;}}intmain(){try{func();}catch(const exception& e){
        cout << e.what()<< endl;}return0;}

运行结果:

在这里插入图片描述

  我们可以看到,申请了 1898M,大概是 1.85G 。要知道,32 位下的虚拟内存总共也就 4G(

      2 
     
    
      32 
     
    
   
     b 
    
   
     i 
    
   
     t 
    
   
  
    2^{32} bit 
   
  
232bit)。其中内核空间就占 1G,栈才分 8M。因此堆空间给了 1.85G,已经很大方了。

  在外面平时的练习中,一般是不会申请控价失败的,空间开辟失败一般是因为申请的空间太大。我们平时练习可以不捕获异常,但是在项目中都是需要捕获异常的。

4 operater new 与 operator delete 函数

     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 和  
 
  
   
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete 是用户进行动态内存申请和释放的操作符

  C++ 提供了两个全局函数:

      o 
     
    
      p 
     
    
      e 
     
    
      r 
     
    
      a 
     
    
      t 
     
    
      o 
     
    
      r 
     
    
   
     operator 
    
   
 operator 
  
   
    
    
      n 
     
    
      e 
     
    
      w 
     
    
   
     new 
    
   
 new 和  
  
   
    
    
      o 
     
    
      p 
     
    
      e 
     
    
      r 
     
    
      a 
     
    
      t 
     
    
      o 
     
    
      r 
     
    
   
     operator 
    
   
 operator 
  
   
    
    
      d 
     
    
      e 
     
    
      l 
     
    
      e 
     
    
      t 
     
    
      e 
     
    
   
     delete 
    
   
 delete。

  
   
    
    
      n 
     
    
      e 
     
    
      w 
     
    
   
     new 
    
   
 new 在底层调用  
   
    
     
     
       o 
      
     
       p 
      
     
       e 
      
     
       r 
      
     
       a 
      
     
       t 
      
     
       o 
      
     
       r 
      
     
    
      operator 
     
    
  operator 
   
    
     
     
       n 
      
     
       e 
      
     
       w 
      
     
    
      new 
     
    
  new 来申请空间; 
  
   
    
    
      d 
     
    
      e 
     
    
      l 
     
    
      e 
     
    
      t 
     
    
      e 
     
    
   
     delete 
    
   
 delete 在底层调用  
   
    
     
     
       o 
      
     
       p 
      
     
       e 
      
     
       r 
      
     
       a 
      
     
       t 
      
     
       o 
      
     
       r 
      
     
    
      operator 
     
    
  operator 
   
    
     
     
       d 
      
     
       e 
      
     
       l 
      
     
       e 
      
     
       t 
      
     
       e 
      
     
    
      delete 
     
    
  delete 来释放空间

  下面是

     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 和  
 
  
   
   
     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete 的源码,我们简单来看一下:
/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/void* __CRTDECL operatornew(size_t size)_THROW1(_STD bad_alloc){// try to allocate size bytesvoid* p;while((p =malloc(size))==0)if(_callnewh(size)==0){// report no memory// 如果申请内存失败了,这里会抛出bad_alloc 类型异常staticconst std::bad_alloc nomem;_RAISE(nomem);}return(p);}

  对于

     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new,我们可以看到在第五行:当
malloc = 0

,则后面的

     i 
    
   
     f 
    
   
  
    if 
   
  
if 语句就抛异常。 
  
   
    
    
      o 
     
    
      p 
     
    
      e 
     
    
      r 
     
    
      a 
     
    
      t 
     
    
      o 
     
    
      r 
     
    
   
     operator 
    
   
 operator 
  
   
    
    
      n 
     
    
      e 
     
    
      w 
     
    
   
     new 
    
   
 new 的本质其实就是 调用  
   
    
     
     
       m 
      
     
       a 
      
     
       l 
      
     
       l 
      
     
       o 
      
     
       c 
      
     
    
      malloc 
     
    
  malloc 函数。
/*
operator delete: 该函数最终是通过free来释放空间的
*/voidoperatordelete(void* pUserData){
    _CrtMemBlockHeader* pHead;RTCCALLBACK(_RTC_Free_hook,(pUserData,0));if(pUserData ==NULL)return;_mlock(_HEAP_LOCK);/* block other threads */
    __TRY
        /* get a pointer to memory block header */
        pHead =pHdr(pUserData);/* verify block type */_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));_free_dbg(pUserData, pHead->nBlockUse);
    __FINALLY
        _munlock(_HEAP_LOCK);/* release other threads */
    __END_TRY_FINALLY
        return;}/*
free的实现
*/#definefree(p)_free_dbg(p, _NORMAL_BLOCK)//free其实是一个宏函数

  对于

      o 
     
    
      p 
     
    
      e 
     
    
      r 
     
    
      a 
     
    
      t 
     
    
      o 
     
    
      r 
     
    
   
     operator 
    
   
 operator 
  
   
    
    
      d 
     
    
      e 
     
    
      l 
     
    
      e 
     
    
      t 
     
    
      e 
     
    
   
     delete 
    
   
 delete,我们看到 第16行,调用了一个
_free_dbg

_free_dbg

是什么呢?我们来看到最下面

     f 
    
   
     r 
    
   
     e 
    
   
     e 
    
   
  
    free 
   
  
free,其实  
  
   
    
    
      f 
     
    
      r 
     
    
      e 
     
    
      e 
     
    
   
     free 
    
   
 free 是一个宏,其本质就是 
_free_dbg

。因此

      o 
     
    
      p 
     
    
      e 
     
    
      r 
     
    
      a 
     
    
      t 
     
    
      o 
     
    
      r 
     
    
   
     operator 
    
   
 operator 
  
   
    
    
      d 
     
    
      e 
     
    
      l 
     
    
      e 
     
    
      t 
     
    
      e 
     
    
   
     delete 
    
   
 delete本质就是调用  
   
    
     
     
       f 
      
     
       r 
      
     
       e 
      
     
       e 
      
     
    
      free 
     
    
  free 函数

  通过上述两个全局函数的实现知道,

      o 
     
    
      p 
     
    
      e 
     
    
      r 
     
    
      a 
     
    
      t 
     
    
      o 
     
    
      r 
     
    
   
     operator 
    
   
 operator 
  
   
    
    
      n 
     
    
      e 
     
    
      w 
     
    
   
     new 
    
   
 new 实际也是通过  
  
   
    
    
      m 
     
    
      a 
     
    
      l 
     
    
      l 
     
    
      o 
     
    
      c 
     
    
   
     malloc 
    
   
 malloc 来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。 
  
   
    
    
      o 
     
    
      p 
     
    
      e 
     
    
      r 
     
    
      a 
     
    
      t 
     
    
      o 
     
    
      r 
     
    
   
     operator 
    
   
 operator 
  
   
    
    
      d 
     
    
      e 
     
    
      l 
     
    
      e 
     
    
      t 
     
    
      e 
     
    
   
     delete 
    
   
 delete 最终是通过  
  
   
    
    
      f 
     
    
      r 
     
    
      e 
     
    
      e 
     
    
   
     free 
    
   
 free 来释放空间的。

  其实,

     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 和  
 
  
   
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete 并没有那么神奇,他们并没有单独搞一套新的申请空间方式,

  
   
    
    
      n 
     
    
      e 
     
    
      w 
     
    
   
     new 
    
   
 new 其本质上也是调用  
  
   
    
    
      m 
     
    
      a 
     
    
      l 
     
    
      l 
     
    
      o 
     
    
      c 
     
    
   
     malloc 
    
   
 malloc,只是它没有直接调,而是调用套着  
 
  
   
   
     m 
    
   
     a 
    
   
     l 
    
   
     l 
    
   
     o 
    
   
     c 
    
   
  
    malloc 
   
  
malloc 马甲的  
 
  
   
   
     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new。

  为什么要套

     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 呢?因为  
 
  
   
   
     m 
    
   
     a 
    
   
     l 
    
   
     l 
    
   
     o 
    
   
     c 
    
   
  
    malloc 
   
  
malloc 失败是直接返回空,C++ 是面向对象的语言,它希望它是按新的一种机制来走,即抛异常。所以抛异常是  
 
  
   
   
     o 
    
   
     p 
    
   
     e 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    opeerator 
   
  
opeerator 
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 抛出来的

5 new 和 delete 的实现原理

5.1 内置类型

  如果申请的是内置类型的空间,

      n 
     
    
      e 
     
    
      w 
     
    
   
     new 
    
   
 new 和  
  
   
    
    
      m 
     
    
      a 
     
    
      l 
     
    
      l 
     
    
      o 
     
    
      c 
     
    
   
     malloc 
    
   
 malloc, 
  
   
    
    
      d 
     
    
      e 
     
    
      l 
     
    
      e 
     
    
      t 
     
    
      e 
     
    
   
     delete 
    
   
 delete 和  
  
   
    
    
      f 
     
    
      r 
     
    
      e 
     
    
      e 
     
    
   
     free 
    
   
 free 基本类似,不同的地方是: 
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
     / 
    
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
     e 
    
   
  
    new/deletee 
   
  
new/deletee 申请和释放的是
单个

元素的空间,

     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new[] 和  
 
  
   
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete[] 申请的是
连续

的空间,而且

     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 在申请空间失败时会
抛异常

     m 
    
   
     a 
    
   
     l 
    
   
     l 
    
   
     o 
    
   
     c 
    
   
  
    malloc 
   
  
malloc 会返回 
NULL

5.2 自定义类型

  •                                          n                               e                               w                                      new                        new 的原理 1、 调用 ```operator new``` 函数```申请空间``` 2、 在申请的空间上执行```构造函数```,完成对象的构造
    
  •                                          d                               e                               l                               e                               t                               e                                      delete                        delete 的原理 1、 在空间上执行```析构函数```,完成对象中资源的清理工作 2、 调用 ```operator delete``` 函数```释放```对象的```空间```
    
  •                                          n                               e                               w                                      new                        new                                             T                               [                               N                               ]                                      T[N]                        T[N] 的原理 1、调用 ```operator new[]``` 函数,在                                              o                               p                               e                               r                               a                               t                               o                               r                                      operator                        operator                                             n                               e                               w                                      new                        new[] 中实际调用                                                   o                                  p                                  e                                  r                                  a                                  t                                  o                                  r                                          operator                           operator                                                  n                                  e                                  w                                          new                           new 函数完成 N 个对象空间的申请 2、在申请的空间上```执行 N 次构造函数```
    
  •                                          d                               e                               l                               e                               t                               e                                      delete                        delete[] 的原理 1、在释放的对象空间上```执行N次析构函数```,完成 N 个对象中的资源清理 2、调用                                              o                               p                               e                               r                               a                               t                               o                               r                                      operator                        operator                                             d                               e                               l                               e                               t                               e                                      delete                        delete[] 释放空间,实际在                                                   o                                  p                                  e                                  r                                  a                                  t                                  o                                  r                                          operator                           operator                                                  d                                  e                                  l                                  e                                  t                                  e                                          delete                           delete[] 中调用                                                   o                                  p                                  e                                  r                                  a                                  t                                  o                                  r                                          operator                           operator                                                  d                                  e                                  l                                  e                                  t                                  e                                          delete                           delete 来释放空间
    

我们可以通过汇编代码来看一下

     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 的底层
intmain(){
    A* p =newA(1);delete p;return0;}

在这里插入图片描述

在这里插入图片描述

6 不匹配使用的情况

6.1new / free

6.1.1 内置类型

intmain(){int* p1 =newint;free(p1);return0;}

  对内置类型,如果我

     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 出来,再对  
 
  
   
   
     f 
    
   
     r 
    
   
     e 
    
   
     e 
    
   
  
    free 
   
  
free 释放,会发生什么?

无事发生
  这里我们要看他的本质。

     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 本质是调用  
 
  
   
   
     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 加调用构造,  
 
  
   
   
     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 本质是调用  
 
  
   
   
     m 
    
   
     a 
    
   
     l 
    
   
     l 
    
   
     o 
    
   
     c 
    
   
  
    malloc 
   
  
malloc,而内置类型没有构造函数的概念

 
  
   
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete 本质是调用  
 
  
   
   
     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete 加调用析构, 
 
  
   
   
     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete 本质是 
 
  
   
   
     f 
    
   
     r 
    
   
     e 
    
   
     e 
    
   
  
    free 
   
  
free,而对内置类型没有调用析构的概念。

  因此,程序不会报错,也不会存在内存泄漏的情况

6.1.2 自定义类型

  那对自定义类型呢

intmain(){
    A* p1 =new A;free(p1)$;return0;}

  首先程序是不会报错的,但相比用

     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete,少调用了一个析构函数,有可能会造成资源没有释放,有内存泄漏的风险

6.2 new [ ] / delete

  现在,我用

     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new[] 申请多个空间,再用  
 
  
   
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete 释放会有问题吗

6.2.1 内置类型

intmain(){int* p1 =newint[10];delete p1;return0;}

  是没有问题的,因为

     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new[] 本质是调用  
 
  
   
   
     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new[], 
 
  
   
   
     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new[] 中又调用  
 
  
   
   
     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new,最终是  
 
  
   
   
     m 
    
   
     a 
    
   
     l 
    
   
     l 
    
   
     o 
    
   
     c 
    
   
  
    malloc 
   
  
malloc

 
  
   
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete[] 本质是调用  
 
  
   
   
     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete[], 
 
  
   
   
     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete[] 内调用  
 
  
   
   
     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete,本质是  
 
  
   
   
     f 
    
   
     r 
    
   
     e 
    
   
     e 
    
   
  
    free 
   
  
free,不涉及什么析构

 
  
   
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete[] 只不过是  
 
  
   
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete 多套了一层壳而已。

  内置类型不会报错,也不会造成内存泄漏。

6.2.2 自定义类型

  那如果我是自定义类型呢?

classB{private:int _b1 =1;int _b2 =2;};intmain(){
    B* p1 =new B[10];delete p1;return0;}

运行结果:

在这里插入图片描述

 可以看到,也是没问题的

 但对类型A呢?

classA{public:A(int a =0):_a(a){
        cout <<"A(int a)"<< endl;}~A(){
        cout <<"~A()"<< endl;}voidPrint(){
        cout <<"_a = "<< _a << endl;}private:int _a =0;int _b =1;};intmain(){
    A* p1 =new A[10];delete p1;return0;}

在这里插入图片描述

崩溃了!
  为什么呢?同样是自定义类型怎么就区别对待呢?

  先问一下,对 类A 和 类B,

     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new[] 应该申请多少空间呢?应该是 **80** 个

我们先看类B:
  通过汇编代码,我们知道

     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new[] 申请了 80 字节

在这里插入图片描述

再看看类A:

在这里插入图片描述

发现A多开了4个字节!

  为什么呢?

  编译器对于开辟多个对象的数组时,

一般会在头上多开辟 4 个字节

,用来存储对象的个数,以便

     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete[] 知道
需要调用几次析构函数

,就像上述开 A 的空间一样。但是编译器对 B

并没有额外开 4 个字节

在这里插入图片描述

  但是,对

类A

,编译器返回的是

pa2

位置的地址;对

类B

是正常返回

pb1

位置地址

  接着,用

     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete 将 类A 和 类B 释放,类B 的释放是没有问题的;但是 类A 就有大问题了: 
 
  
   
   
     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete 收到的是 
pa2 位置的地址

,便从 pa2 开始释放空间,但一整块申请的空间只能整块释放,不能从中间释放,因此报错

  而用

     d 
    
   
     e 
    
   
     l 
    
   
     e 
    
   
     t 
    
   
     e 
    
   
  
    delete 
   
  
delete[] 释放,它会
往前偏移4个字节再释放

,就不会出现这样的问题。

  那为什么 类B 不多开 4 个字节呢?因为编译器对B进行了

优化

,编译器看到 类B

没有去写析构函数

,认为没有资源需要去释放,。所以编译器直接就不调用析构函数了,既然不用调用析构函数,那头上也就不用多开 4 个字节存个数了。

  如果 B 将析构函数加上,也会多开 4 个字节存个数,也会崩溃

7 定位 new 表达式(placement-new)(了解)

7.1 定位 new 用法

     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 是一个**全局函数**,我们可以显示的去调用:
intmain(){
    A* p1 =newA(1);

    A* p2 =(A*)operatornew(sizeof(A));return0;}
     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 底层就是  
 
  
   
   
     m 
    
   
     a 
    
   
     l 
    
   
     l 
    
   
     o 
    
   
     c 
    
   
  
    malloc 
   
  
malloc,因此除了函数名不一样,用法基本是一样的。区别是它不用检查返回值,因为它是抛异常

在这里插入图片描述

  区别是用

      n 
     
    
      e 
     
    
      w 
     
    
   
     new 
    
   
 new 会自动调用构造函数, 
  
   
    
    
      o 
     
    
      p 
     
    
      e 
     
    
      r 
     
    
      a 
     
    
      t 
     
    
      o 
     
    
      r 
     
    
   
     operator 
    
   
 operator 
  
   
    
    
      n 
     
    
      e 
     
    
      w 
     
    
   
     new 
    
   
 new 只开了空间

  那如果我现在想对一块已经存在的空间显示去调用构造函数该怎么做呢?
使用定位new表达式(placement-new)

格式

  •                                               n                                  e                                  w                                  (                                  p                                  l                                  a                                  c                                  e                                          new (place                           new(place_                                                  a                                  d                                  d                                  r                                  e                                  s                                  s                                  )                                  t                                  y                                  p                                  e                                          address) type                           address)type 或者                                                   n                                  e                                  w                                  (                                  p                                  l                                  a                                  c                                  e                                          new (place                           new(place_                                                  a                                  d                                  d                                  r                                  e                                  s                                  s                                  )                                  t                                  y                                  p                                  e                                  (                                  i                                  n                                  i                                  t                                  i                                  a                                  l                                  i                                  z                                  e                                  r                                          address) type(initializer                           address)type(initializer -                                                   l                                  i                                  s                                  t                                  )                                          list)                           list)
    
  •                                          p                               l                               a                               c                               e                                      place                        place_                                             a                               d                               d                               r                               e                               s                               s                                      address                        address 必须是一个指针,                                             i                               n                               i                               t                               i                               a                               l                               i                               z                               e                               r                                      initializer                        initializer -                                              l                               i                               s                               t                                      list                        list 是类型的初始化列表
    
intmain(){
    A* p1 =(A*)operatornew(sizeof(A));new(p1)A(1);//定位new,初始化return0;}

  那析构函数怎么调用呢?

  对已经分配空间的对象,想构造函数只能通过

定位new

,但析构函数是可以我们显式调用的

intmain(){
    A* p1 =(A*)operatornew(sizeof(A));new(p1)A(1);

    p1->~A();operatordelete p1;return0;}

7.2 定位 new 用途

  我们要用

     o 
    
   
     p 
    
   
     e 
    
   
     r 
    
   
     a 
    
   
     t 
    
   
     o 
    
   
     r 
    
   
  
    operator 
   
  
operator 
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 呢?直接用  
 
  
   
   
     n 
    
   
     e 
    
   
     w 
    
   
  
    new 
   
  
new 不好吗,这主要是用于内存池。

  编程中有一种技术叫池化技术,其包括内存池、线程池、连接池……
  池化技术都是为了提高效率。

  那内存池是什么呢?
  假设我现在有一个工程,需要

频繁地申请释放内存

,这样效率难免会较低,毕竟实际中往往是多个任务同时进行。这时,就可以从内存(堆)中专门取出一部分空间,专供这个项目使用。申请从内存池中取空间,释放将空间换给内存池

在这里插入图片描述

  但现在还有一个问题:内存池中申请的只有空间,并没有对其初始化,这时

定位new

就有用武之地了。释放空间时,也是我们自己先调用析构函数在释放。

8 malloc/free 和 new/delete 的区别

  1.                                          m                               a                               l                               l                               o                               c                               /                               f                               r                               e                               e                                      malloc/free                        malloc/free 和                                              f                               r                               e                               e                                      free                        free 是函数,                                              n                               e                               w                               /                               d                               e                               l                               e                               t                               e                                      new/delete                        new/delete 是操作符
    
  2.                                          m                               a                               l                               l                               o                               c                                      malloc                        malloc 申请的空间不会初始化,                                             n                               e                               w                                      new                        new可以初始化
    
  3.                                          m                               a                               l                               l                               o                               c                                      malloc                        malloc 申请空间时,需要```手动计算空间大小并传递```,                                             n                               e                               w                                      new                        new 只需```在其后跟上空间的类型```即可,如果是多个对象 [] 中```指定对象个数即可```
    
  4.                                          m                               a                               l                               l                               o                               c                                      malloc                        malloc 的返回值为                                              v                               o                               i                               d                               ∗                                      void*                        void∗ ,在使用时```必须强转```,                                             n                               e                               w                                      new                        new 不需要,因为                                              n                               e                               w                                      new                        new 后跟的是空间的类型
    
  5.                                          m                               a                               l                               l                               o                               c                                      malloc                        malloc 申请空间失败时,返回的是 ```NULL```,因此使用时必须判空,                                             n                               e                               w                                      new                        new 不需要,但是                                              n                               e                               w                                      new                        new 需要捕获异常
    
  6. 申请自定义类型对象时, m a l l o c / f r e e malloc/free malloc/free 只会开辟空间,不会调用构造函数和析构函数,而 n e w new new在申请空间后会调用构造函数完成对对象的初始化, d e l e t e delete delete 在释放空间前会调用析构函数完成空间中资源的清理释放

好啦,本期关于内存管理的知识就介绍到这里啦,希望本期博客能对你有所帮助。同时,如果有错误的地方请多多指正,让我们在C语言的学习路上一起进步!

标签: c++ jvm 开发语言

本文转载自: https://blog.csdn.net/yusjushd/article/details/140925912
版权归原作者 9毫米的幻想 所有, 如有侵权,请联系我们删除。

“【C++】—— 内存管理”的评论:

还没有评论