• C++中的自定义内存管理


    1,问题:

        1,new 关键字创建出来的对象位于什么地方?

           1,位于堆空间;

        2,有没有可能位于其它地方?

           1,有;

           2,通过一些方式可以使动态创建的对象位于静态存储区;

           3,这个存储区在程序结束后释放;

       

    2,new/delete 被忽略的事实:

        1,new/delete 的本质是 C++ 预定义的操作符;

           1,new/delete 是关键字,但本质是预定义的操作符;

           2,C++ 中操作符可以重载;

        2,C++ 对这两个操作符做了严格的行为定义;

           1,new:

               1,获取足够大的内存空间(默认为堆空间);

               2,在获取的空间中调用构造函数创建对象;

           2,delete:

               1,调用析构函数销毁对象;

               2,归还对象所占用的空间(默认为堆空间);

        3,在 C++ 中能够重载 new/delete 操作符:

           1,全局重载(不推荐);

               1,实际工程开发中不建议这样做

           2,局部重载(针对具体类型进行重载);

               1,针对具体的类重载;

           3,重载 new/delete 的意义在于改变动态对象创建时的内存分配方式;

               1,可以将创建的对象放到其它的内存空间里面去;

        4,new/delete 的重载方式:

           1,代码示例:

    复制代码
     1 // static member function
     2 void* operator new(unsinged int size)  // 第一步获取内存,参数表示需要获取的内存大小;
     3 {
     4      void* ret = NULL;
     5                   
     6     /* ret point to allocated memory */  // 第二步在内存中调用构造函数创建对象;
     7                   
     8     return ret;
     9 }
    10 
    11 // static member function
    12 void operator delete (void* p)  // p 指针指向对应的对象地址,也就是要释放的地址;
    13 {
    14     /* free the memory which is pointed by p */
    15 }
    复制代码

           2,通过函数来对这两个操作符进行重载;

           3,一般针对具体类来重载,所以说 new/delete 的重载函数就是类的成员函数,并且这两个重载函数默认为静态成员函数,写不写 static 都是静态成员函数;

          

    3,静态存储区中创建动态对象编程实验:

    复制代码
     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 class Test
     7 {
     8     static const unsigned int COUNT = 4;
     9     
    10     static char c_buffer[];  // 本质是这里申请空间而下面只是标记使用而已;
    11     static char c_map[];
    12     
    13     int m_value;
    14 public:
    15     void* operator new (unsigned int size)
    16     {
    17         void* ret = NULL;  // 如果这片内存已经满了,返回空;
    18         
    19         /* 查找在 c_buffer 里面那些位置是空闲的,可以用来创建 Test 对象 */
    20         for(int i=0; i<COUNT; i++)
    21         {
    22             if( !c_map[i] )  // 当前空间不可用了;
    23             {
    24                 c_map[i] = 1;  // 标记为不可用;
    25                 
    26                 ret = c_buffer + i * sizeof(Test);  // 查找 c_buffer 这片可用内存空间的首地址,并返回这片空间;
    27                 
    28                 cout << "succeed to allocate memory: " << ret << endl;
    29                 
    30                 break;
    31             }
    32         }
    33         
    34         return ret;
    35     }
    36     
    37     void operator delete (void* p)
    38     {
    39         if( p != NULL )  // 空指针时候什么都不处理;
    40         {
    41             char* mem = reinterpret_cast<char*>(p);
    42             int index = (mem - c_buffer) / sizeof(Test);  // 得到要释放的动态对象在 c_map 中的位置; 
    43             int flag = (mem - c_buffer) % sizeof(Test);  // 这些位置必须是固定的,如果 flag 不为 0,指针则不合法;
    44             
    45             if( (flag == 0) && (0 <= index) && (index < COUNT) )
    46             {
    47                 c_map[index] = 0; // 释放这个地址,即标记这个地址可用;
    48                 
    49                 cout << "succeed to free memory: " << p << endl;
    50             }
    51         }
    52     }
    53 };
    54 
    55 char Test::c_buffer[sizeof(Test) * Test::COUNT] = {0};  // 定义一块静态的内存空间,内存空间想要存储的是 Test 对象,最多存储 4 个 Test 对象;
    56 char Test::c_map[Test::COUNT] = {0};  // 标记数组,用于标记在那些位置已经创建了对象,作用是标记;
    57 
    58 int main(int argc, char *argv[])
    59 {
    60     cout << "===== Test Single Object =====" << endl;
    61      
    62     Test* pt = new Test;  // 这里是在 c_buffer 里面的静态存储区当中的空间生成的;
    63     
    64     delete pt;
    65     
    66     cout << "===== Test Object Array =====" << endl;
    67     
    68     Test* pa[5] = {0};
    69     
    70     for(int i=0; i<5; i++)
    71     {
    72         pa[i] = new Test;
    73         
    74         cout << "pa[" << i << "] = " << pa[i] << endl;
    75     }
    76     
    77     for(int i=0; i<5; i++)
    78     {
    79         cout << "delete " << pa[i] << endl;
    80         
    81         delete pa[i];
    82     }
    83     
    84     return 0;
    85 }
    复制代码

        1,结论:

           1,new/delete 关键字是可以重载的;

           2,重载的意义是改变内存的分配方式,使得动态创建的对象不再位于堆空间里面;

           3,这个实验位于自定义的静态存储区里面的 c_buffer 数组当中; 

        2,拓展:

           1,工程中可以结合不同方法来应用 new/delete 特性;

           2,将本实验的方法和二阶构造法结合在一起,我们就可以创建一个类,并且规定这个类最多产生多少个对象;

           3,单例模式仅仅使得一个类只有一个对象存在,而这里的方法加上二阶构造就可以诞生 N 例模式;

       

    4,问题:

        1,如何在指定的地址上创建 C++ 对象?

           1,我们已经掌握了在静态存储区里面创建对象,是否可以扩展下这个方法,在任意的地址上创建对象呢?

           2,通过重载 new/delete 也许就可以在指定的地址上创建对象;

       

    5,设计思路:

        1,在类中重载 new/delete 操作符;

        2,在 new 的操作符重载函数中返回指定的地址;

        3,在 delete 操作符重载中标记对应的地址可用;

       

    6,自定义动态对象的存储空间编程实验:

    复制代码
      1 #include <iostream>
      2 #include <string>
      3 #include <cstdlib>
      4 
      5 using namespace std;
      6 
      7 class Test
      8 {
      9     static unsigned int c_count;  // 动态实时做决定,所以这个地方就不能有常量;
     10     static char* c_buffer;
     11     static char* c_map;
     12     
     13     int m_value;
     14 public:
     15     /* 动态指定想在什么类型上指定申请对象 */
     16     static bool SetMemorySource(char* memory, unsigned int size)   
     17     {
     18         bool ret = false;  // 返回值为 bool 类型,告诉函数调用者,当前动态空间设置是否成功;
     19         
     20         c_count = size / sizeof(Test);  // 计算传进来的空间可以创建多少对象;
     21         
     22         ret = (c_count && (c_map = reinterpret_cast<char*>(calloc(c_count, sizeof(char)))));
     23         
     24         if( ret )  // 空间至少为 1,且标记指针合法;
     25         {
     26             c_buffer = memory;  // 将指定空间设置到 c_buffer 上;
     27         }
     28         else  // 一切清零;
     29         {
     30             free(c_map);
     31             
     32             c_map = NULL;
     33             c_buffer = NULL;
     34             c_count = 0;
     35         }
     36         
     37         return ret;
     38     }
     39     
     40     void* operator new (unsigned int size)
     41     {
     42         void* ret = NULL;
     43         
     44         /* 有指定的一个具体空间,通过各种计算和验证,看下所指定的空间上面是否可以动态创建对象,标准是 c_count 大于 0,此时意味着通过 setMemorySource() 所指定的空间是可以创建 Test 对象的,则走 if 路径,否则的话,走 else 路径,通过 malloc() 函数得到一片空间; */
     45         if( c_count > 0 )  // 
     46         {
     47             for(int i=0; i<c_count; i++)
     48             {
     49                 if( !c_map[i] )
     50                 {
     51                     c_map[i] = 1;
     52                     
     53                     ret = c_buffer + i * sizeof(Test);
     54                     
     55                     cout << "succeed to allocate memory: " << ret << endl;
     56                     
     57                     break;
     58                 }
     59             }
     60         }
     61         else
     62         {
     63             ret = malloc(size);  // 没有指定具体的在那个空间上生成对象时,通过 malloc 来申请默认的堆空间;
     64         }
     65         
     66         return ret;
     67     }
     68     
     69     void operator delete (void* p)
     70     {
     71         if( p != NULL )
     72         {
     73             if( c_count > 0 )
     74             {
     75                 char* mem = reinterpret_cast<char*>(p);
     76                 int index = (mem - c_buffer) / sizeof(Test);
     77                 int flag = (mem - c_buffer) % sizeof(Test);
     78                 
     79                 if( (flag == 0) && (0 <= index) && (index < c_count) )
     80                 {
     81                     c_map[index] = 0;
     82                     
     83                     cout << "succeed to free memory: " << p << endl;
     84                 }
     85             }
     86             else
     87             {
     88                 free(p);  // 和上面对应
     89             }
     90         }
     91     }
     92 };
     93 
     94 unsigned int Test::c_count = 0;
     95 char* Test::c_buffer = NULL;
     96 char* Test::c_map = NULL;
     97 
     98 int main(int argc, char *argv[])
     99 {
    100     char buffer[12] = {0};  // 定义一片栈上空间,用于存放对象;
    101     
    102     Test::SetMemorySource(buffer, sizeof(buffer));
    103     
    104     cout << "===== Test Single Object =====" << endl;
    105      
    106     Test* pt = new Test;
    107     
    108     delete pt;
    109     
    110     cout << "===== Test Object Array =====" << endl;
    111     
    112     Test* pa[5] = {0};
    113     
    114     for(int i=0; i<5; i++)  // 只有 3 个对象的空间,则后两个对象指向为 NULL;
    115     {
    116         pa[i] = new Test;
    117         
    118         cout << "pa[" << i << "] = " << pa[i] << endl;
    119     }
    120     
    121     for(int i=0; i<5; i++)
    122     {
    123         cout << "delete " << pa[i] << endl;
    124         
    125         delete pa[i];
    126     }
    127     
    128     return 0;
    129 }
    复制代码

        1,通过重载 new/delete,我们可以在任意指定的位置动态创建 C++ 对象;

       

    7,new[]/delete[] 与 new/delete 完全不同:

        1,动态对象数组创建通过 new[] 完成;

        2,动态对象数组的销毁通过 delete[] 完成;

        3,new[]/delete[] 能够被重载,进而改变内存管理方式;

           1,这是两个新的操作符;

       

    8,new[]/delete[] 的重载方式:

        1,代码示例:

    复制代码
     1 // static member function
     2 void* operator new[] (unsigned int size)
     3 {
     4     rerurn malloc(size);
     5 }
     6        
     7 // static member function
     8 void operator delete[] (void* p)
     9 {
    10      free(p);
    11 }
    复制代码

        2,通过类的静态成员函数来重载,不写 static,这两个成员函数在类中也是      静态的;

     

    9,注意事项:

        1,nwe[] 实际需要返回的内存空间可能比期望的要多;

           1,需要额外的空间来保存数组的信息;

           2,如数组长度信息,因为编译器要自动的为我们调用构造函数和析构函数,不保存长度信息,编译器不知道要调用多少次构造函数和析构函数;

        2,对象数组占用的内存中需要保存数组信息;

        3,数组信息用于确定构造函数和析构函数的调用次数;

       

    10,动态数组的内存管理编程实验:

    复制代码
     1 #include <iostream>
     2 #include <string>
     3 #include <cstdlib>
     4 
     5 using namespace std;
     6 
     7 class Test
     8 {
     9     int m_value;
    10 public:
    11     Test()
    12     {
    13         m_value = 0;
    14     }
    15     
    16     ~Test()
    17     {
    18     }
    19     
    20     void* operator new (unsigned int size)
    21     {
    22         cout << "operator new: " << size << endl;
    23         
    24         return malloc(size);
    25     }
    26     
    27     void operator delete (void* p)
    28     {
    29         cout << "operator delete: " << p << endl;
    30         
    31         free(p);
    32     }
    33     
    34     void* operator new[] (unsigned int size)
    35     {
    36         cout << "operator new[]: " << size << endl;
    37         
    38         return malloc(size);
    39     }
    40     
    41     void operator delete[] (void* p)
    42     {
    43         cout << "operator delete[]: " << p << endl;
    44         
    45         free(p);
    46     }
    47 };
    48 
    49 int main(int argc, char *argv[])
    50 {
    51     Test* pt = NULL;
    52     
    53     pt = new Test;  // operator new: 4;
    54      
    55     delete pt;  // operator delete: 0x8e5d008;
    56     
    57     pt = new Test[5];  // operator new[]: 24;这里多了四个字节,用于保存数组的大小信息,因为编译器自动为我们自调用构造函数和析构函数;
    58     
    59     delete[] pt;  // operator delete[]: 0x8e5d018;
    60     
    61     return 0;
    62 }
    复制代码

        1,new/delete 和 new[]/delete[] 是完全不同的;

        2,通过重载的方式说明了它们的不同;

        3,意味着在实际的工程里面,有可能在 new 中函数的内存分配方式和 delete[] 中函数内存分配方式是不一样的,因此必须成对使用,必须要匹配;

        4,假设 new[] 动态创建数组是从栈上分配的空间,然后 delete 想要将空间归还到堆空间去,如果交叉使用,则意味着有可能把栈上的空间归还到堆空间上,程序会崩溃,所以要成对出现,不要交叉使用,因为它们 new/delete 和 new[]/delete[] 完全不同;

       

    11,小结:

        1,new/delete 的本质为操作符;

        2,可以通过全局函数重载 new/delete(不推荐);

        3,可以针对具体的类重载new/delete;

        4,new[]/delete[] 与 new/delete 完全不同;

    此文为作者学习唐佐林老师的学习笔记,仅为交流共享之用,由此带来的后果,与作者无关;转载请注明转载出处;难免有错,欢迎指正,联系方式qunchao24@sina.com。
  • 相关阅读:
    算法分析与设计C++ 并查集
    算法分析与设计 并查集
    算法分析与设计C++ 第四章:动态规划 (附4:过河卒)
    算法分析与设计C++ 2:递归:爬楼梯
    算法分析与设计C++ 1:猴子吃桃
    算法分析与设计C++ 寻找中位数 (快速排序版)
    算法设计与分析C++ 第三章: 递归与分治策略(附众数与重数 非分治实现等算法)
    算法分析与设计C++ 大整数数组汉诺塔双塔实现
    算法分析与设计C++ 第一章:递推算法(附汉诺塔递归递推实现)
    算法分析与设计C++ 第二章:STL
  • 原文地址:https://www.cnblogs.com/sharecenter/p/14710188.html
Copyright © 2020-2023  润新知