zoukankan      html  css  js  c++  java
  • delete &operator delete 【转】

    对象析构谈—— delete this 的使用及注意事项

    In order to understand "delete this" :

    First Step - dive into "delete p"

    delete p 执行了哪些步骤?
    delete p 是一个两步的过程:调用析构函数,然后释放内存。delete p产生的代码看上去是这样的(假设是Object*类型的):

    delete原语可以看作如下这样一个过程:
    p->~Object();
    p->operator delete(p);

    p->~Object() 语句调用p指向的Object对象的析构函数。
    p->operator delete(p) 语句调用对象p的内存释放原语 void operator delete(void* p)。如果没有实现该方法,将调用系统的内存释放原语::operator delete(ptr)做释放该对象内存的操作。当然细节上并不这么简单,我们最后的实验部分会详细讨论

    Second Step - "delete this"

    成员函数调用delete this合法吗?
    只要你小心,一个对象请求自杀(delete this),是可以的。

    以下是我对“小心”的定义:

    你必须100%的确定,this对象是用 new分配的(不是用new[],也不是用定位放置 new,也不是一个栈上的局部对象,也不是全局的,也不是另一个对象的成员,而是明白的普通的new)。

    你必须100%的确定,该成员函数是this对象最后调用的的成员函数。 

    你必须100%的确定,剩下的成员函数(delete this之后的)不接触到this对象任何一块(包括调用任何其他成员函数或访问任何数据成员)。

    你必须100%的确定,在delete this之后不再去访问this指针。换句话说,你不能去检查它,将它和其他指针比较,和NULL比较,打印它,转换它,对它做任何事。

    自然,对于这种情况还要习惯性地告诫:当你的指针是一个指向基类类型的指针,而没有虚析构函数时(也不可以delete this)。

    注意:因为是在类成员函数里面delete this的,所以在此语句以后,不能访问任何的成员变量及函数(调用虚函数必须对象实例存在以检查类型),否则一定非法。

    上面所说的在执行时不一定会报错,但尽量不要这么做。

    Some test examples:

    析构函数本身是不会释放内存的, 
    除非在析构函数里面显示的使用delete操作符. 

    在对类指针使用delete时,实际发生了两个步骤。 
    先是调用该类的析构函数,以做数据成员的释放工作,以及一些finish code,这一切由程序员自己定义。 
    然后再调用operator delete(void*)释放该对象实例的内存数据。这是一个对象在消亡之前的所做的最后动作。一般不要override这个函数,如果要,务必记住最后调用系统的::operator delete真正释放该对象所占用的内存。
    一般来说,内存释放释放的只能是数据段的内容(包括堆和栈,但释放栈上的内存由系统进行),而代码段的内存,除一些病毒攻击等非正常强行改写手段外,在内存中是永远不会释放/改变的,直到程序结束,因此在内存释放后也是可以访问的。所以,一般所谓的释放内存delete操作,是在数据段进行的释放

    可以试试下面的代码
    Example 1: 两步操作
    class x { 
    public : 
            x(){} 
            ~x() { 
                    printf("%s ","~x()"); 
            }
    }; 

    void main() { 
            x* p=new x; 
            ::operator delete(p); //调用delete内存释放原语,不会调用~x(),如果确实调用了系统::operator delete,就没有内存泄露(也可能由用户函数覆盖)
            delete p; //~x()依然会执行,operator delete中将会报错(最后将讨论) 



    Example 2: override重写的operator delete
    class x { 
    public : 
            x(){
            } 
            ~x() { 
                    printf("~x() ");
                    //delete p; //这里若进行此操作则会陷入嵌套
            } 
            void operator delete(void * ptr) { 
                    printf("x::delete() "); 
            }
    }; 

    void main() { 
            x* p=new x; 
            delete p; //依次调用p的~x()和operator delete
            delete p; //不会报错,因为"operator delete" override了系统函数,没有进行::operator delete(this)操作。
            delete p; //同理依然不会报错 



    Example 3: 默认的operator delete
    class x { 
    public : 
            x(){
                    //delete p; //构造时delete不会报错,只要确保以后不会用到该实例(包括delete p)。
            } 
            ~x() { 
                    printf("~x() ");
            } 
    };

    void main() { 
            x* p=new x; 
            delete p; //依次调用p的~x()和operator delete(其中调用了系统的::operator delete)
            //delete p; //报错,这里没有override,对象调用的是系统的::operator delete 
    }

    进一步分析:

    让我们看一下系统::operator delete的内部实现(in dbgdel.cpp):
    void operator delete(
            void *pUserData
            )
    {
            _CrtMemBlockHeader * pHead;

            RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));

            if (pUserData == NULL)
                return;

            _mlock(_HEAP_LOCK); /* block other threads */
            __TRY

                /* get a pointer to memory block header */
                pHead = pHdr(pUserData);

                 /* verify block type */
                _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)); //检查该段内存是否被程序占用。一般出现的如果内存已经释放了,又执行内存释放操作,这里就会报错

                _free_dbg( pUserData, pHead->nBlockUse );

            __FINALLY
                _munlock(_HEAP_LOCK); /* release other threads */
            __END_TRY_FINALLY

            return;
    }
    如果在里面设置断点,无论是直接调用::operator delete还是类似delete p调用对象的operator delete,如果没有人为override,都会进入这个函数,进行释放内存的操作。因此一个C++的类其实可以看做是有一个类似java的Object在内部进行操控:

    class object { 
    public :
            object() { }
            ~object() { }
            void operator delete(void *ptr) {
                    ::operator delete(ptr);
            }
    };

    delete原语看起来会是如下的样子:
    p->~object();
    object::operator delete(p);

    因为代码段的内存是不会被释放的,因此无论对象p的内存有没有释放,这两个语句都会执行,不会因为p没有指向任何存在的对象而报错,只是在最后执行到::operator delete的时候,才会在执行_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse))的时候报错。

    Example 4: 代码段不会被释放
    class x { 
    public : 
        x(){printf("x() "); } 
        ~x() { 
            printf("~x() ");
            operator delete(this);
            //以下并不会报错
            int n=~get(34);//返回-6(~5)
            x(); //会连带调用x()->~x()->operator delete
        } 
        void operator delete(void *p) { //这其实是一个静态函数,成员函数调用不能放在里面
            ::operator delete(p);
            printf("x::delete() ");
        }
        int get(int ) { //注意这个申明是合法的
            printf("x::get() ");
            return 5;
        }
    };

    int main()
    {
        x* p=new x;
        x::x(); //合法的,会连带调用x()->~x()->operator delete,但会因为具体对象不存在而报错
        //p->x(); //调用非法,编译不通过
        delete p;
        int num = p->get(1); //不会报错,返回5
    }

    Example 5: 释放内存只释放数据段
    class x {
    public :
        int n;
        x() {
            printf("x() ");
            n=5;
        }
        ~x() { 
            printf("~x() ");
        }
        void operator delete(void *p) { //这其实是一个静态函数,成员函数调用不能放在里面
            ::operator delete(p);
            printf("x::delete() ");
        }
        int get(int ) {
            printf("x::get() ");
            return n;
        }
        void test() {
            delete this;
            //以下并不会报错
            int n=this->get(34); //会得到一个非法值,不是5
        }
    };

    int main()
    {
        x* p=new x;
        p->test();
        delete p;
        int num = p->get(1); //会得到一个非法值,不是5
    }

    以上实验在VS 2005下测试通过

  • 相关阅读:
    振动传感器
    水银开关式碰撞传感器
    倾斜传感器
    光敏传感器
    激光传感器的使用
    html字符串 转 json
    浏览器播放视频加快进功能
    处理箭头函数单个参数括号规则冲突
    VUE-directive指令之语法、引用、钩子函数、参数
    win10系统自动升级怎么还原到以前的系统
  • 原文地址:https://www.cnblogs.com/cavehubiao/p/3310660.html
Copyright © 2011-2022 走看看