• 异常处理、动态内存申请在不同编译器之间的表现差异


    续上节内容 c++中的异常处理 ...

    目录

      1、在main() 函数中抛出异常会发生什么

      2、在析构函数中抛出异常会发生什么

      3、函数的异常规格说明

        4、动态内存申请结果的分析

        5、关于 new 关键字的新用法

    1、在main() 函数中抛出异常会发生什么

      由上节中的 异常抛出(throw exception)的逻辑分析  可知,异常抛出后,会顺着函数调用栈向上传播,在这期间,若异常被捕获,则程序正常运行;若异常在 main() 函数中依然没有被捕获,也就是说在 main() 函数中抛出异常会发生什么呢?(程序崩溃,但因编译器的不同,结果也会略有差异)  

     1 #include <iostream>
     2 #include <cstdlib>
     3 
     4 using namespace std;
     5 
     6 class Test
     7 {
     8 public:
     9     Test()
    10     {
    11         cout << "Test()" << endl;
    12     }
    13     
    14     ~Test()
    15     {
    16         cout << "~Test()" << endl;
    17     }
    18 };
    19 
    20 int main()
    21 {
    22     cout << "main() begin..." << endl;
    23 
    24     static Test t;
    25     
    26     throw 1;  
    27               
    28     cout << "main() end..." << endl;
    29           
    30     return 0;
    31 }
    在 main() 函数中抛出异常

      将上述代码在不同的编译器上运行,结果也会不同;

      在 g++下运行,结果如下:

    main() begin...

    Test()

    terminate called after throwing an instance of 'int'

    Aborted (core dumped)

      在 vs2013下运行,结果如下:

      main() begin...

      Test()

      弹出异常调试对话框

       从运行结果来看,在 main() 中抛出异常后会调用一个全局的 terminate() 结束函数,在 terminal() 函数中不同编译器处理的方式有所不同。

      c++ 支持自定义结束函数,通过调用 set_terminate() 函数来设置自定义的结束函数,此时系统默认的 terminal() 函数就会失效

     (1)自定义结束函数的特点:与默认的 terminal() 结束函数 原型一样,无参无返回值;

         关于使用 自定义结束函数的注意事项:

        1)不能在该函数中再次抛出异常,这是最后一次处理异常的机会了;

        2)必须以某种方式结束当前程序,如 exit(1)、abort();

        exit():结束当前的程序,并且可以确保所有的全局对象和静态局部对象全部都正常析构;

        abort():异常终止一个程序,并且异常终止的时候不会调用任何对象的析构函数;

     (2)set_terminate() 函数的特点:1)参数类型为函数指针 void(*)();2)返回值为自定义的 terminate() 函数入口地址;

     1 #include <iostream>
     2 #include <cstdlib>
     3 
     4 using namespace std;
     5 
     6 class Test
     7 {
     8 public:
     9     Test()
    10     {
    11         cout << "Test()" << endl;
    12     }
    13     
    14     ~Test()
    15     {
    16         cout << "~Test()" << endl;
    17     }
    18 };
    19 
    20 void mterminate()
    21 {
    22     cout << "void mterminate()" << endl;
    23     abort();   // 异常终止一个程序,不会析构任何对象
    24     //exit(1); // 结束当前程序,但会析构所有的全局和静态局部对象
    25 }
    26 
    27 int main()
    28 {
    29     terminate_handler f = set_terminate(mterminate);
    30     
    31     cout << "terminate() 函数的入口地址 = " << f << "::" << mterminate << endl;
    32     
    33     cout << "main() begin..." << endl;
    34  
    35     static Test t;  
    36     
    37     throw 1;  
    38               
    39     cout << "main() end..." << endl;
    40           
    41     return 0;
    42 }
    43 /**
    44  * 以 exit(1) 结束程序时的运行结果:
    45  * terminate() 函数的入口地址 = 1::1
    46  * main() begin...
    47  * Test()
    48  * void mterminate()
    49  * ~Test()
    50  */
    51 
    52 /**
    53  * 以 abort() 结束程序时的运行结果:
    54  * terminate() 函数的入口地址 = 1::1,《为什么全局函数的地址都是 1 ?》
    55  * main() begin...
    56  * Test()
    57  * void mterminate()
    58  * Aborted (core dumped)
    59  */
    自定义结束函数测试案例

    2、在析构函数中抛出异常会发生什么

      一般而言,在析构函数中销毁所使用的资源,若在资源销毁的过程中抛出异常,那么会导致所使用的资源无法完全销毁;若对这一解释深入挖掘,那么会发生什么呢?

      试想程序在 main() 函数中抛出了异常,然而该异常并没有被捕获,那么该异常就会触发系统默认的结束函数 terminal();因为不同编译器对 terminal() 函数的内部实现有所差异,

     (1)若 terminal() 函数是以 exit(1) 这种方式结束程序的话,那么就会有可能调用到析构函数,而此时的析构函数中又抛出了一个异常,就会导致二次调用 terminal() 函数,后果不堪设想(类似堆空间的二次释放),但是,强大的 windows、Linux系统会帮我们解决这个问题,不过在一些嵌入式的操作系统中可能就会产生问题。

     (2)若 terminal() 函数是以 abort() 这种方式结束程序的话,就不会发生(1)中的情况,这就是 g++ 编译器为什么会这么做的原因了。

      注:terminal() 结束函数是最后处理异常的一个函数,所以该函数中不可以再次抛出异常,而(1)中就是违反了这条规则;

        若在 terminal() 结束函数中抛出异常,就会导致二次调用 terminal() 结束函数。

      结论:在析构函数中抛出异常时,若 terminate() 函数中以 exit() 这种方式结束程序的话会很危险,有可能二次调用 terminate() 函数,甚至死循环。

     1 #include <iostream>
     2 #include <cstdlib>
     3 
     4 using namespace std;
     5 
     6 class Test
     7 {
     8 public:
     9     Test()
    10     {
    11         cout << "Test()" << endl;
    12     }
    13     
    14     ~Test()
    15     {
    16         cout << "~Test()" << endl;
    17 
    18         throw 1// 代码分析:会二次调用 mterminate()
    19     }
    20 };
    21 
    22 void mterminate()
    23 {
    24     cout << "void mterminate()" << endl;
    25     exit(1); // 结束当前程序,但会析构所有的全局和静态局部对象
    26 }
    27 
    28 int main()
    29 {
    30     set_terminate(mterminate);
    31 
    32     cout << "main() begin..." << endl;
    33 
    34     static Test t;  
    35     
    36     throw 1;  
    37               
    38     cout << "main() end..." << endl;
    39           
    40     return 0;
    41 }
    在析构函数中抛出异常案例测试

       将上述代码在不同的编译器上运行,结果也会不同;

      在 g++下运行,结果如下:

    main() begin...

    Test()

    void mterminate()      // 在 main() 函数中第一次抛出异常,调用 自定义结束函数 mterminate()

    ~Test()          // exit(1) 程序结束时,调用了析构函数,在析构函数中再次抛出了异常,会调用 abort() 函数

    Aborted (core dumped)    // 注:一些旧版本的编译器可能会调用 自定义结束函数 mterminate(),此行显示 void mterminate()

      在 vs2013下运行,结果如下:

    main() begin...

    Test()

    void mterminate()

    ~Test()          // exit(1) 程序结束时,调用了析构函数,在析构函数中再次抛出了异常,会 弹出异常调试对话框

    弹出异常调试对话框         // 注:一些旧版本的编译器可能会调用 自定义结束函数 mterminate(),此行显示 void mterminate()

      结论:新版本的编译器对 析构函数中抛出异常这种行为 做了优化,直接让程序异常终止。

    3、函数的异常规格说明

      如何判断某个函数是否会抛出异常,或许有很多办法,如查看函数的实现(可惜第三方库不提供函数实现)、查看技术文档(可能查看的文档与当前所使用的函数版本不一致),但刚才列举的这些方法都会存在缺陷。其实有一种更为简单高效的方法,就是直接通过异常声明来判断这个函数是否会抛出异常,简称为函数的异常规格说明

      异常声明作为函数声明的修饰符,写在参数列表的后面;  

    1 /* 可能抛出任何异常 */
    2 void func1();
    3 
    4 /* 只能抛出的异常类型:char 和 int */
    5 void func2() throw(char, int);
    6 
    7 /* 不抛出任何异常 */
    8 void func3() throw();

      异常规格说明的意义:

         (1)提示函数调用者必须做好异常处理的准备;(如果想知道调用的函数会抛出哪些类型的异常时,只用打开头文件看看这个函数是怎么声明的就可以了;)

      (2)提示函数的维护者不要抛出其它异常;

           (3)异常规格说明是函数接口的一部分;(用于说明这个函数如何正确的使用;)

     1 #include <iostream>
     2 
     3 using namespace std;
     4 
     5 void func() throw(int)
     6 {
     7     cout << "func()" << endl;
     8     
     9     throw 'c';
    10 }
    11 
    12 int main()
    13 {
    14     try 
    15     {
    16         func();
    17     } 
    18     catch(int) 
    19     {
    20         cout << "catch(int)" << endl;
    21     } 
    22     catch(char) 
    23     {
    24         cout << "catch(char)" << endl;
    25     }
    26 
    27     return 0;
    28 }
    异常规格之外的异常测试案例

    将上述代码在不同的编译器上运行,结果也会不同;

      在 g++下运行,结果如下:

    func()

    terminate called after throwing an instance of 'char'

    Aborted (core dumped)

      在 vs2013下运行,结果如下:

    func()

    catch(char)  // 竟然捕获了该异常,说明不受异常规格说明限制

      通过对上述代码结果的再次研究,我们发现在 g++中,当异常不在函数异常规格说明中,就会调用一个 全局函数 unexpected(),在该函数中再调用默认的全局结束函数 terminate();

      但在 vs2013中,异常并不会受限于函数异常规格说明的限制。

      结论:g++ 编译器遵循了c++规范,然而 vs2013 编译器并不受限于这个约束。

      提示:不同编译器对函数异常规格说明的处理方式有所不同,所以在进行项目开发时,有必要测试当前所使用的编译器。

      c++ 中支持自定义异常函数;通过调用 set_unexpected() 函数来设置自定义异常函数,此时系统默认的 全局函数 unexpected() 就会失效

        (1)自定义异常函数的特点:与默认的 全局函数 unexpected() 原型一样,无参无返回值;

        (2)关于使用 自定义异常函数 的注意事项:

        可以在函数中抛出异常(当异常符合触发函数的异常规格说明时,恢复程序执行;否则,调用全局 terminate() 函数结束程序);

        (3)set_unexpected() 函数的特点:1)参数类型为函数指针 void(*)();2)返回值为自定义的 unexpected() 函数入口地址;

     1 #include <iostream>
     2 #include <cstdlib>
     3 
     4 using namespace std;
     5 
     6 void m_unexpected()
     7 {
     8     cout << "void m_unexpected()" << endl;
     9   
    10     throw 1;  // 2 这个异常符合异常规格说明,所以可以被捕获
    11     // terminate(); // 若这么写,与上个程序的运行结果相同
    12 }
    13 
    14 void func() throw(int)
    15 {
    16     cout << "func()" << endl;
    17     
    18     throw 'c';  // 1 由于不符合异常规格说明,此时会调用 m_unexpected() 函数
    19 }
    20 
    21 int main()
    22 {
    23     set_unexpected(m_unexpected);
    24     
    25     try 
    26     {
    27         func();
    28     } 
    29     catch(int) 
    30     {
    31         cout << "catch(int)" << endl;
    32     } 
    33     catch(char) 
    34     {
    35         cout << "catch(char)" << endl;
    36     }
    37 
    38     return 0;
    39 }
    自定义 unexpected() 函数的测试案例

      将上述代码在不同的编译器上运行,结果也会不同;

      在 g++下运行,结果如下:

    func()

    void m_unexpected()

    catch(int)  // 由于自定义异常函数 m_unexpected() 中抛出的异常 throw 1 符合函数异常规格说明,所以该异常被捕获

        在 vs2013下运行,结果如下:

    func()

    catch(char)  // vs2013 没有遵循c++规范,不受异常规格说明的限制,直接捕获函数异常规格说明中 throw ‘c’这个异常

      结论:(g++)unexpected() 函数是正确处理异常的最后机会,如果没有抓住,terminate() 函数会被调用,当前程序以异常告终;

         (vs2013)没有函数异常规格说明的限制,所有的函数都可以抛出任意异常。

     4、动态内存申请结果的分析

      在 c 语言中,使用 malloc 函数进行动态内存申请时,若成功,则返回对应的内存首地址;若失败,则返回 NULL 值。

      在 c++规范中,通过重载 new、new[] 操作符去动态申请足够大的内存空间时,

      (1)若成功,则在获取的空间中调用构造函数创建对象,并返回对象地址;

      (2)若失败(内存空间不足),根据编译器的不同,结果也会不同;

        1)返回 NULL 值;(早期编译器的行为,不属于 c++ 规范)

        2)抛出 std::bad_alloc 异常;(后期的编译器会抛出异常,一些早期的编译器依然返回 NULL 值)

        注:不同编译器  对如何抛出异常  也是不确定的,c++ 规范是在 new_handler() 函数中抛出 std::bad_alloc 异常,而 new_handler() 函数是在内存申请失败时自动调用的。

       当内存空间不足时,会调用全局的 new_hander() 函数,调用该函数的意义就是让我们有机会整理出足够的内存空间;所以,我们可以自定义 new_hander() 函数,并通过全局函数 set_new_hander() 去设置自定义 new_hander() 函数。(通过实验证明, 有些编译器没有定义全局的 new_hander() 函数,比如 vs2013、g++ ,见案例1 )

      特别注意:set_new_hander() 的返回值是默认的全局 new_hander() 函数的入口地址。

            而 set_terminate() 函数的返回值是自定义 terminate() 函数的入口地址;

            set_unexpected() 函数的返回值是自定义 unexpected() 函数的入口地址。

     1 #include <iostream>
     2 
     3 using namespace std;
     4 
     5 void my_new_handler()
     6 {
     7     cout << "void my_new_handler()" << endl;
     8 }
     9 
    10 int main(int argc, char *argv[])
    11 {
    12     // 若编译器有全局 new_handler() 函数,则 func != NULL,否则,func == NULL;
    13     new_handler func = set_new_handler(my_new_handler);
    14 
    15     try
    16     {
    17         cout << "func = " << func << endl;  
    18         
    19         if( func )
    20         {
    21             func();
    22         }
    23     }
    24     catch(const bad_alloc&)
    25     {
    26         cout << "catch(const bad_alloc&)" << endl;
    27     }
    28     
    29     return 0;
    30 }
    案例1:证明 编译器是否定义了全局 new_handler() 函数

      将上述代码在不同的编译器上运行,结果也会不同;

      在 vs2013 和 g++下运行,结果如下:

    func = 0   // => vs2013 and g++ 中没有定义 全局 new_handler() 函数 

       在 BCC下运行,结果如下:

    func = 00401468

    catch(const bad_alloc&)  // 在 BCC 中定义了全局 new_handler() 函数,并在该函数中抛出了 std::bad_alloc 异常

     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 (size_t size)
    25     {
    26         cout << "operator new: " << size << endl;
    27         
    28         return NULL;
    29     }
    30     
    31     void operator delete (void* p)
    32     {
    33         cout << "operator delete: " << p << endl;
    34         
    35         free(p);
    36     }
    37     
    38     void* operator new[] (size_t size)
    39     {
    40         cout << "operator new[]: " << size << endl;
    41         
    42         return NULL;
    43     }
    44     
    45     void operator delete[] (void* p)
    46     {
    47         cout << "operator delete[]: " << p << endl;
    48         
    49         free(p);
    50     }
    51 };
    52 
    53 int main(int argc, char *argv[])
    54 {
    55     Test* pt = new Test();  
    56     
    57     cout << "pt = " << pt << endl;
    58     
    59     delete pt;
    60     
    61     pt = new Test[5];
    62     
    63     cout << "pt = " << pt << endl;
    64     
    65     delete[] pt; 
    66     
    67     return 0;
    68 }
    案例2:不同编译器在内存申请失败时的表现

      将上述代码在不同的编译器上运行,结果也会不同;

      在 g++下运行,结果如下: 

    operator new: 4

    Test()  // 由于堆空间申请失败,返回 NULL 值,接着又在这片失败的空间上创建对象,当执行到 m_value = 0;时(相当于在 非法地址上赋值),编译器报 段错误

    Segmentation fault (core dumped)

       在 vs2013下运行,结果如下:

    operator new: 4

    pt = 00000000

    operator new[]: 24

    pt = 00000000

      在 BCC下运行,结果如下: 

    operator new: 4

    pt = 00000000

    operator new[]: 24

    pt = 00000000

    operator delete[]: 00000000

       总结:在 g++ 编译器中,内存空间申请失败,也会继续调用构造函数创建对象,这样会产生 段错误;在 vs2013、BCC 编译器中,内存空间申请失败,直接返回NULL。

       为了让不同编译器在内存申请时的行为统一,所以必须要重载 new、delete 或者 new[]、delete[] 操作符,当内存申请失败时,直接返回 NULL 值,而不是抛出 std::bad_alloc 异常,这就必须通过 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 (size_t size) throw()
    25     {
    26         cout << "operator new: " << size << endl;
    27         
    28         return NULL;
    29     }
    30     
    31     void operator delete (void* p)
    32     {
    33         cout << "operator delete: " << p << endl;
    34         
    35         free(p);
    36     }
    37     
    38     void* operator new[] (size_t size) throw()
    39     {
    40         cout << "operator new[]: " << size << endl;
    41         
    42         return NULL;
    43     }
    44     
    45     void operator delete[] (void* p)
    46     {
    47         cout << "operator delete[]: " << p << endl;
    48         
    49         free(p);
    50     }
    51 };
    52 
    53 int main(int argc, char *argv[])
    54 {
    55     Test* pt = new Test();
    56     
    57     cout << "pt = " << pt << endl;
    58     
    59     delete pt;
    60     
    61     pt = new Test[5];
    62     
    63     cout << "pt = " << pt << endl;
    64     
    65     delete[] pt; 
    66     
    67     return 0;
    68 }
    案例3:(优化)不同编译器在内存申请失败时的表现

      通过测试,g++、vs2013、BCC 3款编译器的运行结果一样,输出结果如下:

    operator new: 4

    pt = 00000000

    operator new[]: 24

    pt = 00000000

    5、关于 new 关键字的新用法

    (1)nothrow 关键字

     1 #include <iostream>
     2 #include <exception>
     3 
     4 using namespace std;
     5 
     6 void func1()
     7 {
     8     try
     9     {
    10         int* p = new(nothrow) int[-1]; 
    11         
    12         cout << p << endl;
    13         
    14         delete[] p; 
    15     }
    16     catch(const bad_alloc&)
    17     {
    18         cout << "catch(const bad_alloc&)" << endl;
    19     }    
    20     
    21     cout << "--------------------" << endl;
    22     
    23     try
    24     {
    25         int* p = new int[-1];
    26         
    27         cout << p << endl;
    28         
    29         delete[] p; 
    30     }
    31     catch(const bad_alloc&)
    32     {
    33         cout << "catch(const bad_alloc&)" << endl;
    34     }    
    35 }
    36 
    37 int main(int argc, char *argv[])
    38 {
    39     func1();
    40     
    41     return 0;
    42 }
    nothrow 关键字的使用

      将上述代码在不同的编译器上运行,结果也会不同;

      在 g++、BCC下运行,结果如下:

    0                // 使用了 nothrow 关键字,在动态内存申请失败时,直接返回 NULL
    --------------------
    catch(const bad_alloc&)  // 没有 nothrow 关键字,动态内存申请失败时,抛出 std::bad_alloc 异常

        在 vs2013下编译失败:

    原因是 内存申请太大,即数组的总大小不得超过 0x7fffffff 字节;

      结论:nothrow 关键字的作用:无论动态内存申请结果是什么,都不会抛出异常,并且这种现象在不同编译器之间也会有差异。

    (2)通过 new 在指定的地址上创建对象

     1 #include <iostream>
     2 
     3 using namespace std;
     4 
     5 void func2()
     6 {
     7     int bb[2] = {0};
     8     
     9     struct ST
    10     {
    11         int x;
    12         int y;
    13     };
    14     
    15     // 通过 new 在指定的地址上创制对象
    16     // 将动态内存ST 创建到栈空间上(int bb[2] = {0}),但要保证二者的内存模型相同,此处是 8 bytes
    17     ST* pt = new(bb) ST();  
    18     
    19     pt->x = 1;
    20     pt->y = 2;
    21     
    22     cout << bb[0] << "::" << bb[1] << endl;
    23     
    24     bb[0] = 3;
    25     bb[1] = 4;
    26     
    27     cout << pt->x << "::" << pt->y << endl;
    28     
    29     pt->~ST();  // 由于指定了创建对象的空间,必选显示的调用析构函数
    30 }
    31 
    32 int main(int argc, char *argv[])
    33 {   
    34     func2();
    35     
    36     return 0;
    37 }
    通过 new 在指定的地址上创制对象

      在 g++、vs2013、BCC下运行,结果如下:

    1::2

    3::4

     动态内存申请的结论:

        (1)不同的编译器在动态内存分配上的实现细节不同;

        (2)编译器可能重定义 new 的实现,并在实现中抛出 bad_alloc 异常;(vs2013、g++)

        (3)编译器的默认实现中,可能没有设置全局的 new_handler() 函数;(vs2013、g++)

        (4)对于移植性要求高的代码,需要考虑 new 的具体细节;

      我们可以进一步验证上述结论,就以 vs2013 举例,在编译器的安装包找到 new.cpp、new2.cpp 这两个文件(文 件路径:C:Program Files (x86)Microsoft Visual Studio 12.0VCcrtsrc),分析其源码发现,在内存申请失败时,会调用 _callnewh(cb) 函数,该函数可以通过如下方式查看:https://docs.microsoft.com/zh-cn/cpp/c-runtime-library/reference/callnewh?view=vs-2015

      

       

      所以在 vs中,当动态内存申请失败时,会抛出 std::bad_alloc异常,而不会返回 NULL 值;

     1 #ifdef _SYSCRT
     2 #include <cruntime.h>
     3 #include <crtdbg.h>
     4 #include <malloc.h>
     5 #include <new.h>
     6 #include <stdlib.h>
     7 #include <winheap.h>
     8 #include <rtcsup.h>
     9 #include <internal.h>
    10 
    11 // 两个版本的 new 实现方式,失败时都会抛出 bad_alloc 异常
    12 void * operator new( size_t cb )
    13 {
    14     void *res;
    15 
    16     for (;;) {
    17 
    18         //  allocate memory block
    19         res = _heap_alloc(cb);
    20 
    21         //  if successful allocation, return pointer to memory
    22 
    23         if (res)
    24             break;
    25 
    26         //  call installed new handler
    27         if (!_callnewh(cb))  // 申请失败,则抛出 bad_alloc 异常
    28             break;
    29 
    30         //  new handler was successful -- try to allocate again
    31     }
    32 
    33     RTCCALLBACK(_RTC_Allocate_hook, (res, cb, 0));
    34 
    35     return res;
    36 }
    37 #else  /* _SYSCRT */
    38 
    39 #include <cstdlib>
    40 #include <new>
    41 
    42 _C_LIB_DECL
    43 int __cdecl _callnewh(size_t size) _THROW1(_STD bad_alloc);
    44 _END_C_LIB_DECL
    45 
    46 void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
    47         {       // try to allocate size bytes
    48         void *p;
    49         while ((p = malloc(size)) == 0)
    50                 if (_callnewh(size) == 0)
    51                 {       // report no memory
    52                         _THROW_NCEE(_XSTD bad_alloc, );
    53                 }
    54 
    55         return (p);
    56         }
    源码分析 new.cpp
     1 #include <cruntime.h>
     2 #include <malloc.h>
     3 #include <new.h>
     4 #include <stdlib.h>
     5 #include <winheap.h>
     6 #include <rtcsup.h>
     7 
     8 void *__CRTDECL operator new(size_t) /*_THROW1(std::bad_alloc)*/;
     9 
    10 void * operator new[]( size_t cb )
    11 {
    12     void *res = operator new(cb);
    13 
    14     RTCCALLBACK(_RTC_Allocate_hook, (res, cb, 0));
    15 
    16     return res;
    17 }
    源码分析 new2.cpp

      

  • 相关阅读:
    IO输入输出流
    IDEA项目创建的保存方式
    JAVA File类的学习
    构造方法私有化
    写好文献综述的5个技巧
    毕业设计:文献参考(20)
    毕业设计:文献参考(19)
    毕业设计:文献参考(18)
    毕业设计:文献参考(17)
    毕业设计:文献参考(16)
  • 原文地址:https://www.cnblogs.com/nbk-zyc/p/12536982.html
Copyright © 2020-2023  润新知