• 安全delete,添加refenerce,release


    #ifndef SAFE_ADDREF
    #define SAFE_ADDREF(p)    if (p != NULL) { p->AddRef(); }
    #endif

    #ifndef SAFE_RELEASE
    #define SAFE_RELEASE(p)    if (p != NULL) { p->Release(); p = NULL; }
    #endif


    #define SAFE_DELETE(x)                {if ((x)!=NULL) {delete (x); (x)=NULL;}}
    #define SAFE_DELETEBUFF(x)            {if((x)!=NULL) {delete [] (x); (x) = NULL;}}

    #define SAFE_FREE(x)            {if((x)!=NULL) {free(x); (x) = NULL;}}

    int *b = new int[4];
        b[0] = 1;
        b[1] = 2;
        b[2] = 3;
        b[3] = 4;
        b[4] = '';

    delete[]  b;

    b = NULL

        char *array = (char*) malloc(sizeof(char) * 5);
        array[0] = 'a';
        array[1] = 'b';
        array[2] = 'c';
        array[3] = 'd';
        array[4] = '';

        free(array);
        array = NULL;

    https://blog.csdn.net/shandaliuyan/article/details/5930719

    <<c++ primer>>练习   14.11   中提到: 
    Account   *parray=new   Account[100]; 
    delete   parray; 
    delete   []   parray; 
    方括号的存在会使编译器获取数组大小(size)然后析构函数再被依次应用在每个元素上,一共size次。否则,只有一个元素被析构。 
    无论哪种情况,分配的全部空间被返还给自由存储区。 

    我的问题是:为什么无论哪种情况,分配的全部空间被返还给自由存储区? 
    对于delete parray,为什么不是删除单个Account元素,而是删除了100个. 
    编译器怎么知道parray这个指针实际指向的是数组还是单个元素,即便知道指向的是自由存储区的数组,这个数组的大小又怎么知道。 
    难道是编译器辅助行为? 

    总结:空间释放(肯定有个log记录分配的大小)和调用析构函数(类型识别,不同的编译器实现不同)采用不同的机制. 

    (1)一般在分配时分配器会自动写一个日志(一般在分配使用得内存之前又一个结构),用于记录分配的大小,分配内容的sizeof等等。 
    直观得想想,delete和delete[]都是传入一个void*如果不保存日志就无法知道分配时到底是分配了一个还是多个单元. 
    所以虽然delete和delete[]不同但是分配器在执行释放过程中都会读取这个日志,从而了解到底应该释放多少内存,但是从程序员的角度来说,既然分配了数组,就应该用delete[] 

    (2)在VC下用汇编跟过delete[]的执行情况,发现这个 "日志 "就是一个4字节长的整数记录数组元素个数,紧挨在数组第一个元素之前.   
    但是有个前提:对象类型(或其基类)有显式析构函数.换句话说,析构函数是非trivial的.   
    否则的话,数组前面是没有这个日志的.其实对于析构函数是trivial的情况,delete[]时无需调用其析构函数,因此此时VC把delete[]当做delete同样处理. 

    (3)因为释放数组空间和为数组调用析构函数是两个独立的部分,可以使用不同的机制来实现。 
    释放空间的机制是需要绝对保证的。因此,即使你不写delete[],它也会将所有空间释放,其机制可以是前置的长度信息,也可以不是(如后置的特征分割符等等)。 
    而调用析构函数可以一般采用前置长度信息的方式(当然也可以有其他方式)。在没有[]提示时,编译器在调用析构就将它当一个元素,而不会使用数组方式来调用每一个析构函数了。 
    LS:“但是有个前提:对象类型(或其基类)有显式析构函数.换句话说,析构函数是非trivial的. 否则的话,数组前面是没有这个日志的.” 
    ——这说明,LS使用的编译器在释放数组空间时,并没有用前置的长度信息的方式。由此可见,释放数组空间和为数组调用析构函数确实可以使用不同的机制 

    (4)delete parray,编译器得到类型信息是Account单个的指针,那么释放时,只调用一次析构函数。 
    delete[] parray,编译器得到的类型信息是Account[]类型,则按照Account数组来处理,依次调用每个元素的析构函数。 

    注意,以上是在编译期间就确定下来的,编译器识别到类型信息的不同会决定调用析构函数的情况有不同。 

    但是对于内存释放,delete操作则不是通过类型信息来确定分配的内存大小,那么内存大小的信息从什么地方得到呢? 
    当我们使用operator new为一个自定义类型对象分配内存时,实际上我们得到的内存要比实际对象的内存大一些,这些内存除了要存储对象数据外,还需要记录这片内存的大小,此方法称为   cookie。这一点上的实现依据不同的编译器不同。(例如   MFC   选择在所分配内存的头部存储对象实际数据,而后面的部分存储边界标志和内存大小信息。g++   则采用在所分配内存的头4个自己存储相关信息,而后面的内存存储对象实际数据。)当我们使用   delete   operator   进行内存释放操作时,delete   operator   就可以根据这些信息正确的释放指针所指向的内存块。 
    对于parray指针,可以根据这样的cookie信息来得到指向内存空间的大小,delete parray和delete[] parray都是一样的,同样一个指针,cookie信息是相同的,所以对应的内存都会被释放掉。但是由于编译器理解两种情况下的类型是不同的,所以调用析构函数会有不同。 

    (5)难道是编译器辅助行为? 
    没错,就是。不同的编译器可能采用的具体方法有可能不一样,但不管采用什么方法,编译器必须记住那块大小。 

    (6)转自<<effective c++>> 
    条款5:对应的new和delete要采用相同的形式 

    下面的语句有什么错? 
    string *stringarray = new string[100]; 
    delete stringarray; 
    一切好象都井然有序——一个new对应着一个delete——然而却隐藏着很大的错误:程序的运行情况将是不可预测的。至少,stringarray指向的100个string对象中的99个不会被正确地摧毁,因为他们的析构函数永远不会被调用。 
    用new的时候会发生两件事。首先,内存被分配(通过operator new 函数,详见条款7-10和条款m8),然后,为被分配的内存调用一个或多个构造函数。用delete的时候,也有两件事发生:首先,为将被释放的内存调用一个或多个析构函数,然后,释放内存(通过operator delete 函数,详见条款8和m8)。对于 delete来说会有这样一个重要的问题:内存中有多少个对象要被删除?答案决定了将有多少个析构函数会被调用。 
    这个问题简单来说就是:要被删除的指针指向的是单个对象呢,还是对象数组?这只有你来告诉delete。如果你在用delete时没用括号,delete就会认为指向的是单个对象,否则,它就会认为指向的是一个数组: 
    string *stringptr1 = new string; 
    string *stringptr2 = new string[100]; 
    ... 
    delete stringptr1;// 删除一个对象 
    delete [] stringptr2;// 删除对象数组 
    如果你在stringptr1前加了"[]"会怎样呢?答案是:那将是不可预测的; 
    如果你没在stringptr2前没加上"[]"又会怎样呢?答案也是:不可预测。 
    int这样的固定类型来说,结果也是不可预测的,即使这样的类型没有析构函数。所以,解决这类问题的规则很简单:如果你调用new时用了[],调用delete时也要用[]。如果调用new时没有用[],那调用delete时也不要用[]。 

     
        #include <iostream>  
          
        using namespace std;  
          
        struct foo  
        {  
            ~foo(){}; //去掉后,就不会记录个数了.  
        };  
          
          
        int main(int argc,char *argv[])  
        {  
          
             foo* f = new foo;  
             delete f;  
             f=0;  
             delete f;  
          
             foo* fa = new foo[8];  
             printf("%u/n", *((char *)fa - 4)); //输出8:辅助析构函数.  
             /* 
             对于有显式析构函数的对象的数组, 
             编译器会在数组前分配4个字节储存数组元素的个数 
             (也就是需要调用析构函数的次数), 
             因为必须知道数组实际的元素合个数, 
             delete[]才能知道需要调用几次析构函数。 
             */  
             delete fa; //有的编译器这里会有异常.  
             //用delete而不是delete[]释放用new[]分配的空间这种行为是undefined的,  
             //也就是由编译器实现所决定的  
             fa = 0;  
             delete fa;  
          
             return 0;  
        }  
  • 相关阅读:
    【CODEVS1380】没有上司的舞会
    【poj2248】Addition Chains
    【poj3070】Fibonacci
    【NOIP2006】开心的金明
    【Tyvj1359】收入计划
    【NOIP2015】跳石头
    【CODEVS1219】骑士游历
    暑假假期总结第六周
    暑假假期总结第五周
    暑假假期总结第四周
  • 原文地址:https://www.cnblogs.com/scotth/p/3163487.html
Copyright © 2020-2023  润新知