zoukankan      html  css  js  c++  java
  • 外传三 动态内存申请的结果

    问题:

    动态内存申请一定成功吗?

     问题:

    new语句中的异常是怎么抛出来的?

     

    一般我们会在new_handler函数中进行内存的整理,整理之后再次申请。

    问题:

    如何跨编译器统一new的行为,提高代码移植性?

    全局定义new就是全局new操作符的重载。

    最后两种方法是推荐的做法。

    使用nothrow时,new失败了会返回空指针。

     示例程序:

     1 #include <iostream>
     2 #include <new>
     3 #include <cstdlib>
     4 #include <exception>
     5 
     6 using namespace std;
     7 
     8 class Test
     9 {
    10     int m_value;
    11 public:
    12     Test()
    13     {
    14         cout << "Test()" << endl;
    15         
    16         m_value = 0;
    17     }
    18     
    19     ~Test()
    20     {
    21         cout << "~Test()" << endl;  
    22     }
    23     
    24     void* operator new (unsigned int size) 25     {
    26         cout << "operator new: " << size << endl;
    27         
    28         // return malloc(size);
    29         
    30         return NULL;
    31     }
    32     
    33     void operator delete (void* p)
    34     {
    35         cout << "operator delete: " << p << endl;
    36         
    37         free(p);
    38     }
    39     
    40     void* operator new[] (unsigned int size) 41     {
    42         cout << "operator new[]: " << size << endl;
    43         
    44         // return malloc(size);
    45         
    46         return NULL;
    47     }
    48     
    49     void operator delete[] (void* p)
    50     {
    51         cout << "operator delete[]: " << p << endl;
    52         
    53         free(p);
    54     }
    55 };
    56 
    57 void my_new_handler()
    58 {
    59     cout << "void my_new_handler()" << endl;
    60 }
    61 
    62 void ex_func_1()
    63 {
    64     new_handler func = set_new_handler(my_new_handler);
    65     
    66     try
    67     {
    68         cout << "func = " << func << endl;
    69         
    70         if( func )
    71         {
    72             func();
    73         }
    74     }
    75     catch(const bad_alloc&)
    76     {
    77         cout << "catch(const bad_alloc&)" << endl;
    78     }
    79 }
    80 
    81 
    82 int main(int argc, char *argv[])
    83 {
    84     ex_func_1();
    85     
    86     return 0;
    87 }

    第64行用于将我们自己的new处理函数设置进去并且返回编译器默认的new处理函数,并赋值给func,运行结果如下:

    打印结果为0,说明了编译器没有默认的new处理函数。

    vs2010的运行结果如下:

    bcc编译器的结果如下:

    可以看到bcc编译器是有默认的new处理函数的。

    而且func函数确实抛出了bad_alloc异常。

    第二个实验函数:

    81行的new行为调用了Test类重载的new操作符,new操作符返回NULL(这是我们人为设置的),并且在NULL指针上执行了构造函数,这个构造函数中有成员变量的赋值操作,因此,导致了段错误。

    上述程序在vs2010上结果如下:

    这说明了如果重载的new操作符返回了NULL,那么是不会调用构造函数的。

    同样的程序,bcc编译器的行为如下:

    和vs的结果是一样的。

    这三个编译器的行为是不统一的。

    因此,我们要对程序进行改进,如下:

     1 #include <iostream>
     2 #include <new>
     3 #include <cstdlib>
     4 #include <exception>
     5 
     6 using namespace std;
     7 
     8 class Test
     9 {
    10     int m_value;
    11 public:
    12     Test()
    13     {
    14         cout << "Test()" << endl;
    15         
    16         m_value = 0;
    17     }
    18     
    19     ~Test()
    20     {
    21         cout << "~Test()" << endl;  
    22     }
    23     
    24     void* operator new (unsigned int size) throw()
    25     {
    26         cout << "operator new: " << size << endl;
    27         
    28         // return malloc(size);
    29         
    30         return NULL;
    31     }
    32     
    33     void operator delete (void* p)
    34     {
    35         cout << "operator delete: " << p << endl;
    36         
    37         free(p);
    38     }
    39     
    40     void* operator new[] (unsigned int size) throw()
    41     {
    42         cout << "operator new[]: " << size << endl;
    43         
    44         // return malloc(size);
    45         
    46         return NULL;
    47     }
    48     
    49     void operator delete[] (void* p)
    50     {
    51         cout << "operator delete[]: " << p << endl;
    52         
    53         free(p);
    54     }
    55 };
    56 
    57 void my_new_handler()
    58 {
    59     cout << "void my_new_handler()" << endl;
    60 }
    61 
    62 void ex_func_1()
    63 {
    64     new_handler func = set_new_handler(my_new_handler);
    65     
    66     try
    67     {
    68         cout << "func = " << func << endl;
    69         
    70         if( func )
    71         {
    72             func();
    73         }
    74     }
    75     catch(const bad_alloc&)
    76     {
    77         cout << "catch(const bad_alloc&)" << endl;
    78     }
    79 }
    80 
    81 void ex_func_2()
    82 {
    83     Test* pt = new Test();
    84     
    85     cout << "pt = " << pt << endl;
    86     
    87     delete pt;
    88     
    89 }
    90 
    91 
    92 int main(int argc, char *argv[])
    93 {
    94     //ex_func_1();
    95     ex_func_2();
    96     
    97     return 0;
    98 }

    我们在第24、40行的重载函数声明时加上了throw(),这就告诉编译器这个函数不抛出异常。

    结果如下:

    这样,这三款编译器的行为就统一了。

    添加申请数组的实验:

      1 #include <iostream>
      2 #include <new>
      3 #include <cstdlib>
      4 #include <exception>
      5 
      6 using namespace std;
      7 
      8 class Test
      9 {
     10     int m_value;
     11 public:
     12     Test()
     13     {
     14         cout << "Test()" << endl;
     15         
     16         m_value = 0;
     17     }
     18     
     19     ~Test()
     20     {
     21         cout << "~Test()" << endl;  
     22     }
     23     
     24     void* operator new (unsigned int size) throw()
     25     {
     26         cout << "operator new: " << size << endl;
     27         
     28         // return malloc(size);
     29         
     30         return NULL;
     31     }
     32     
     33     void operator delete (void* p)
     34     {
     35         cout << "operator delete: " << p << endl;
     36         
     37         free(p);
     38     }
     39     
     40     void* operator new[] (unsigned int size) throw()
     41     {
     42         cout << "operator new[]: " << size << endl;
     43         
     44         // return malloc(size);
     45         
     46         return NULL;
     47     }
     48     
     49     void operator delete[] (void* p)
     50     {
     51         cout << "operator delete[]: " << p << endl;
     52         
     53         free(p);
     54     }
     55 };
     56 
     57 void my_new_handler()
     58 {
     59     cout << "void my_new_handler()" << endl;
     60 }
     61 
     62 void ex_func_1()
     63 {
     64     new_handler func = set_new_handler(my_new_handler);
     65     
     66     try
     67     {
     68         cout << "func = " << func << endl;
     69         
     70         if( func )
     71         {
     72             func();
     73         }
     74     }
     75     catch(const bad_alloc&)
     76     {
     77         cout << "catch(const bad_alloc&)" << endl;
     78     }
     79 }
     80 
     81 void ex_func_2()
     82 {
     83     Test* pt = new Test();
     84     
     85     cout << "pt = " << pt << endl;
     86     
     87     delete pt;
     88     
     89     pt = new Test[5];
     90     
     91     cout << "pt = " << pt << endl;
     92     
     93     delete[] pt; 
     94 }
     95 
     96 int main(int argc, char *argv[])
     97 {
     98     // ex_func_1();
     99     ex_func_2();
    100     
    101     return 0;
    102 }

    结果如下:

    linux:

    vs2010:

    bcc:

     实验三:

    在单次申请的时候告诉编译器,不管结果是什么都不要抛出异常,如果申请失败返回空指针。

     1 #include <iostream>
     2 #include <new>
     3 #include <cstdlib>
     4 #include <exception>
     5 
     6 using namespace std;
     7 
     8 class Test
     9 {
    10     int m_value;
    11 public:
    12     Test()
    13     {
    14         cout << "Test()" << endl;
    15         
    16         m_value = 0;
    17     }
    18     
    19     ~Test()
    20     {
    21         cout << "~Test()" << endl;  
    22     }
    23     
    24     void* operator new (unsigned int size) throw()
    25     {
    26         cout << "operator new: " << size << endl;
    27         
    28         // return malloc(size);
    29         
    30         return NULL;
    31     }
    32     
    33     void operator delete (void* p)
    34     {
    35         cout << "operator delete: " << p << endl;
    36         
    37         free(p);
    38     }
    39     
    40     void* operator new[] (unsigned int size) throw()
    41     {
    42         cout << "operator new[]: " << size << endl;
    43         
    44         // return malloc(size);
    45         
    46         return NULL;
    47     }
    48     
    49     void operator delete[] (void* p)
    50     {
    51         cout << "operator delete[]: " << p << endl;
    52         
    53         free(p);
    54     }
    55 };
    56 
    57 void my_new_handler()
    58 {
    59     cout << "void my_new_handler()" << endl;
    60 }
    61 
    62 void ex_func_3()
    63 {
    64     int* p = new(nothrow) int[10];
    65     
    66     // ... ...
    67     
    68     delete[] p; 
    69     
    70     int bb[2] = {0};
    71     
    72     struct ST
    73     {
    74         int x;
    75         int y;
    76     };
    77     
    78     ST* pt = new(bb) ST();
    79     
    80     pt->x = 1;
    81     pt->y = 2;
    82     
    83     cout << bb[0] << endl;
    84     cout << bb[1] << endl;
    85     
    86     pt->~ST();
    87 }
    88 
    89 int main(int argc, char *argv[])
    90 {
    91 
    92     ex_func_3();
    93     
    94     return 0;
    95 }

    第64行告诉编译器无论成功失败都不要抛出异常,第78行是在指定的位置创建对象,在这里我们是通过new在栈上创建对象,在第86行我们手动的调用析构函数。在这里不能delete pt指针。

    结果如下:

    bcc下结果:

    vs结果:

    可以看到三款编译器的结果全部一致。

    vs2010的第一个版本new实现如下:

    第29行申请代码,第33行判断,申请成功就会跳出去直接返回。第43行的是调试代码,可以忽略。

    申请不成功会走到第37行,会调用到_calnewh函数,_callnewh说明文档中给出如下解释:

    如果没有设置全局的new处理函数,那么就会抛出bad_alloc异常,如果设置的new处理函数没有进行内存整理,那么_callnewh就会返回0,最终返回给用户空指针。

    因此如果我们自定义new_handler函数,就能使new不抛出异常。这个解决方案对于vs编译器是有用的。

    vs中new的另一个实现:

    这个函数就是malloc失败就会进入if语句,然后调用_callnewh函数,如果这个函数没有成功处理内存申请的问题,那么就抛出bad_alloc异常。

    小结:

  • 相关阅读:
    实验一 开发环境的熟悉(小组)
    第六章家庭作业
    Linux常用命令-1
    Linux简介
    Python for写死循环?
    python将某个列表按元素值分成多个子列表
    xshell 5连接NAT模式的虚拟机
    python中remove的一些坑
    Sender IP字段为"0.0.0.0"的ARP请求报文
    免费ARP
  • 原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9610114.html
Copyright © 2011-2022 走看看