zoukankan      html  css  js  c++  java
  • 详解new/delete(整合)

    C++中内存的动态分配与管理永远是一个让C++开发者头痛的问题,本文通过对C++中内存的动态分配释放的基本原理的介绍,让读者朋友能对C++中的内存的动态分配与释放有较为深入的理解,从而更好驾驭C++程序。

    1. 函数(Function)
    (1) operator new function

    1 void * ::operator new(size_t);                    //Global
    2 void * class-name::operator new(size_t);     //Class

    上面是C++中operator new function的原型,一个是全局类型的,一个的类成员类型的。全局类型的operator new函数在下面两种情况下被调用:一种是在分配C++内建(built-in)类型的动态内存时,一种是在分配用户没有自己定义operator new成员函数的用户自定义类型的动态内存时。 如果用户在自己定义的类型中,定义了operator new函数,那么用户在用new申请该类型的动态内存时, 便会调用该类型的成员函数operator new, 而不是全局的operator new。

    另外,我们注意到,上面的原型中函数的返回值为void *类型, 第一个参数为size_t类型,这个是C++编译器要求的,如果要自己重载operator new函数,返回值必须为void* 类型,第一个参数必须为size_t类型,否则,编译器会返回如下错误信息:

     1 error: ‘operator new’ takes type ‘size_t’ (‘unsigned int’) as first parameter 

    这里需要注意的一点是,我们可以利用operator new function可以重载的特点,可以通过参数传入一些额外的信息,来调试程序,检测内存泄露等。比如,我们可以像下面这样重载,传入调用处的行号,函数名,这样就可以跟踪内存的分配使用情况:

    1 void * operator new(size_t unSize, int nLine, const char * pFunc)
    2 {
    3     prinft("Line: %d, Func: %s, allocate %u byte(s)
    ", nLine, pFunc, unSize);
    4     return malloc(unSize);
    5 }

    (2) operator delete function

    1 void operator delete( void * );
    2 void operator delete( void *, size_t );

    上面是operator delete function的原型。operator delete function也有全局的和类成员的两种。这里需要注意,一个类只能有一个operator delete function做为其成员函数,而且必须为上面两种中的其中一种,没有其它的形式。如果一个类实现了自己的operator delete function成员函数,那么在释放该类型的内存时,编译器便会调用成员operator delete function, 而不是全局的。

    上面的两种原型,第一种,在调用的时候,编译器会把要释放的内存的首地址传入,第二种,在调用的时候,编译器会把要释放的内存的首地址和大小都传入。因此,可以利用这一特性,如果我们在基类中实现第二种形式的operator delete function的成员函数,那么便可以用之来释放子类类型的内存(具体参考最后面的例子)。

    2. 运算符(Operator)
    (1) new operator

    [::] new [placement] new-type-name [new-initializer]
    [::] new [placement] ( type-name ) [new-initializer]

    注:上面的'[]’表示在其中的部分是optional(可选的)
    上面是new operator的原型。在C++中,动态内存的分配,通常都是调用new operator来完成的,利用new operator来分配动态内存,编译器要做下面两项工作:

    • a. 调用operator new function分配内存(allocate the memory)
    • b. 调用构造函数(call the constructor)来进行初始化

    下面来说一说new operator的原型中各部分到底是干什么的:
    placement: 如果你重载了operator new function, placement可以用来传递额外的参数
    type-name: 指定要分配的内存的类型,可以是内建(built-in)类型,也可以是用户自定义类型
    new-initializer: 指定对分配后内存的初始化的参数,也就的构造函数的参数 。这里需要注意一点,在分配一个对象的数组类型的内存时,不能够指定初始化参数;换言之,要想分配一个对象的数组类型的内存,该对象必须有缺省构造函数

    (2) delete opeartor

    [::] delete cast-expression
    [::] delete [ ] cast-expression

    上面是delete operator的原型,第一种用来释放普通的对象(包括内建类型)类型的内存,第二种用来释放对象的数组类型的内存。在C++中,用new operator分配的动态内存,必须调用delete operator来释放,通常用delete operator释放内存编译器要做下面两项工作:

    • a. 调用对象析构函数来析构对象
    • b. 调用operator delete function来释放内存(deallocate the memory)


    3. 关于new/delete使用过程中一些需要注意的点
    (1)如何区别operator new/delete function 与 new/delete operator ?
    通过上面的讲述,不难看出,我们分配/释放动态内存,调用的是new/delete operator, 而在调用new/delete的过程中,编译器会自动调用operator new/delete function来完成实际的内存分配/释放的工作

    (2) 用delete operator去释放一块不是由new operator释放的内存,结果是不可预料的,因此,切记,operator new与operator delete一定要配对使用,这是写好程序的基础

    (3) new operator调用失败会抛出std::bad_alloc异常,前提是你没有自己重载对应的operator new function;delete operator失败,常见的原因是多次delete同一块内存

    (4) 如果一块内存被delete后,再对它解引用(Dereference),结果也是不可预测的,很可能导致程序崩溃

    (5) delete一个空(NULL)指针是安全的,没有任何害处的

    (6) 类成员类型的operator new/delete函数必须为静态(static)函数,因此它们不能为虚函数(virtual function),也遵守public, protected, private的访问权限控制

    4. 关于上面所讲的内容的一些例子:

      1 #include <cstdio>
      2 #include <cstdlib>
      3 
      4 void * operator new(size_t unSize)
      5 {
      6     printf("operator new called
    ");
      7     return malloc(unSize);
      8 }
      9 void * operator new[](size_t unSize)
     10 {
     11     printf("operator [] called
    ");
     12     return malloc(unSize);
     13 }
     14 
     15 void * operator new(size_t unSize, int nLine, const char * pFunc)
     16 {
     17     printf("operator new called, line: %d, func: %s
    ",
     18         nLine, pFunc);
     19     return malloc(unSize);
     20 }
     21 //注意:c++14才支持全局delete或delete【】有多个参数,参看参考资料2.下面两个delete不会覆盖全局的。
     22 void operator delete(void * pMem,size_t unSize)
     23 {
     24      printf("delete1: %u
    ", unSize);
     25     free(pMem);
     26 }
     27 void operator delete[](void * pMem, size_t unSize)
     28 {
     29           printf("delete[]: %u
    ", unSize);
     30           free(pMem);
     31  }
     32 
     33 class A
     34 {
     35 public:
     36     
     37     A(int a = 0) :
     38       _a(a)
     39       {   
     40           printf("constructor called
    ");
     41       }  
     42      virtual ~A()
     43       {
     44           printf("~A()
    ");
     45       }
     46       void * operator new(size_t unSize)
     47       {
     48           printf(" calledA
    ");
     49           return malloc(unSize);
     50           
     51       }
     52      //注意:但是支持自定义类型操作符new或delete重载支持size_t参数。即要删除对象的大小delete,要删除对象数组大小delete[].。
     53       void operator delete(void * pMem, size_t unSize)
     54       {
     55           printf("delete2: %u
    ", unSize);
     56           free(pMem);
     57       }
     58       void operator delete[](void * pMem, size_t unSize)
     59       {
     60           printf("delete[]: %u
    ", unSize);
     61           free(pMem);
     62       }
     63       
     64 private:
     65     
     66     int _a;
     67 };
     68 
     69 class B: public A
     70 {
     71 public:
     72     //隐式的为静态函数。
     73     void * operator new(size_t unSize, int nLine, const char * pFunc)
     74     {
     75         printf("operator new called, line: %d, fileB: %s
    ",
     76             nLine, pFunc);
     77         printf("operator new: %u
    ", unSize);
     78         //_b=0;
     79         return malloc(unSize);
     80     }
     81     
     82     ~B()
     83     {
     84         printf("~B()
    ");
     85     }
     86     
     87     int _b;
     88     
     89     int _bb;
     90 };
     91 
     92 int main()
     93 {
     94     A * pA = new A(10);
     95     printf("#######
    ");
     96     
     97     A * pB = new (__LINE__, __FILE__) B();
     98     printf("#######
    ");
     99     
    100     A * szA = new A[10];
    101     B *szB = new B[10];
    102     printf("#######
    ");
    103     
    104     delete pA; 
    105     printf("#######
    ");
    106     
    107     delete pB; 
    108     printf("#######
    ");
    109     
    110     delete [] szA;
    111     printf("#######
    ");
    112     
    113     delete [] szB;
    114     printf("#######
    ");
    115 
    116 //下面两个不是自定义类,没有类重载new.delete故只能调用全局的,本程序全局不支持size_t参数,故只能调用标准C++中的全局operate delete.故不会打印信息。
    117     char * pC = new char[10];
    118     delete [] pC; 
    119 
    120     char *pu = NULL;
    121     delete pu;
    122 }
    clang运行结果如下:

    calledA constructor called #######
    operator new called, operator new: 16 constructor called ####### operator [] called constructor called constructor called constructor called constructor called constructor called constructor called constructor called constructor called constructor called constructor called operator [] called constructor called constructor called constructor called constructor called constructor called constructor called constructor called constructor called constructor called constructor called ####### ~A() delete2: 8 ####### ~B() ~A() delete2: 16 ####### ~A() ~A() ~A() ~A() ~A() ~A() ~A() ~A() ~A() ~A() delete[]: 84//注意84=4+8*10,数组分配的堆空间,第一个int空间放数组个数,接下来顺序放对象 ####### ~B() ~A() ~B() ~A() ~B() ~A() ~B() ~A() ~B() ~A() ~B() ~A() ~B() ~A() ~B() ~A() ~B() ~A() ~B() ~A() delete[]: 164 ####### operator [] called //调用覆盖的全局函数operate new [],打印信息。 //delete[]调用全局的。

    new与delete(new[]与delete[])的调用顺序:类中的重载,本文件中的重载,c++源码的原始操作符函数。

  • 相关阅读:
    WAF、流控设备、堡垒机
    IPS入侵防御系统
    IDS入侵检测系统
    OSI安全体系结构
    边界网关协议BGP
    路由选择协议(RIP/OSPF)
    路由协议之OSPF
    路由协议之RIP
    Social engineering tookit 钓鱼网站
    YII框架中的srbac权限管理模块的安全与使用(版本是1.1.20)
  • 原文地址:https://www.cnblogs.com/loversinJapan/p/4887579.html
Copyright © 2011-2022 走看看