zoukankan      html  css  js  c++  java
  • C++中的 new / delete

    /*****************************************************************************/
    /*                        C++中的 new / delete                               */
    /*****************************************************************************/
    /*
        new的3种形态: new operator , operator new , placement new

        new operator: 
            new操作符,像 + - * / && . :: ?: 等操作符一样,是语言内置的, 它
            不能被重载,不能改变其行为。
            它的行为包括分配内存的 operator new 和调用构造函数的 placement new。
            new operator 实际上做了三件事:获得一块内存空间、调用构造函数、返回
            正确的指针。如果创建的是简单类型(如char)的变量,那么第二步会被省略。
            比如:
                CTest* pT = new CTest(1, 2);
            它的调用实际上等效于:
                void*  p  = operator new( sizeof(CTest) );
                CTest* pT = new(p) CTest(2, 2);
            其中前一句是operator new分配内存,后一句是placement new调用构造函
            数,并返回正确的CTest*指针。


        operator new:
            操作符new,原型为:
                void* operator new(size_t size);
            它分配指定大小的内存, 可以被重载, 可以添加额外的参数, 但第一个参数
            必须为 size_t 。
            它除了被 new operator 调用外也可以直接被调用, 如: 
                void* p = operator new(sizeof(CTest));
            这种用法和调用 malloc 一样, 只分配了sizeof(CTest)大小的内存。
            
        placement new:
            置换new,原型为:
             void operator new(size_t size,void*p);
            它在一块指定的内存上调用构造函数, 包含头文件<new>之后也可
            以直接使用,如:
                CTest* pT = new(p) CTest(2, 2);
            他在p这块内存上调用CTest的构造函数来初始化CTest。
            如果用 placement new 构造出来的对象,必须显示的调用对象的析构函数,
            如:
                pT->~CTest();
            然后释放能存, 调用 operator delete (对应于分配时的 operator new)
                operator delete(pT);


        delete operator:
            delete操作符,和 new operator 一样,不能被重载,不能改变其行为。
            delete operator 做的事有:调用析构函数,然后调用operator delete来
            释放内存,比如:
                delete pT;
            它的调用实际上等效于:
                pT->~CTest();
                operator delete(pT);

        operator delete:
             操作符delete,原型为:
             void*operator delete(void *p)
            同理,对应于分配内存的 operator new,释放内存的为 operator delete ,
            它也可以被重载。
        
        operator new []
        operator delete []
            也是同样原理 ....

    ******************************************************************************/


    /*****************************************************************************/
    /*                        new 的基本使用指南                                 */
    /*****************************************************************************/
    /*
        1)、想在堆上建立一个对象,应该用 new 操作符,它既分配内存又为对象调用构
            造函数。

        2)、如果仅仅想分配内存,就应该调用 operator new 函数;它不会调用构造函数。
        
        3)、如果想定制在堆对象被建立时的内存分配过程,应该重载 operator new 函数,
            然后使用 new operator,new operator 会调用定制的 operator new 。

        4)、如果想在一块已经获得指针的内存里建立一个对象,应该用 placement new 。 
            placement new 主要适用于:
                (a): 对时间要求非常高的应用程序中,因为这些程序分配的时间是确定的;
                (b): 长时间运行而不被打断的程序;
                (c): 以及执行一个垃圾收集器 (garbage collector) 。

        注意:如果用 placement new 构造出来的对象,必须显示的调用对象的析构函数。

    ******************************************************************************/


    /*****************************************************************************/
    /*                          delete this                                      */
    /*****************************************************************************/
    /*
        如上所述,调用 delete pT; 的执行可以看作如下这样一个过程:
            pT->~CTest();
            operator delete(pT);
        其中:
            pT->~CTest() 语句调用析构函数。(析构函数本身是不会释放内存的)
            operator delete(pT) 释放内存。 (这是一个对象在消亡之前的最后动作)
        
            若CTest类重载了operator delete(), 那么将不调用全局的operator delete(pT),
        而是调用 pT->operator delete(pT)(在CTest::operator delete()内最好还应该调用
        全局的operator delete来释放内存)。

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

        以下是对 "小心" 的定义:
      
            1)、必须100%的确定:对象是用 new 分配的(不是用new[],也不是用
                placement new,也不是一个栈上的局部对象,也不是全局的,也不
                是另一个对象的成员,而是明白的普通的 new )。

            2)、必须100%的确定:该成员函数是对象最后调用的的成员函数。

            3)、必须100%的确定:该成员函数在 delete this 之后的代码中不接触到
                对象的任何一块(包括调用任何其他成员函数或访问任何数据成员)。

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

                自然,对于这种情况还要习惯性地告诫:当你的指针是一个指向基类类
                型的指针,而没有虚析构函数时(也不可以delete this)。因为是在类
                成员函数里面delete this的,所以在此语句以后,不能访问任何成员变
                量及虚函数(调用虚函数必须对象实例存在以检查类型),否则一定非法。
        
            上面所说的情况,在执行时不一定会报错,但尽量不要这么做。因为,一般来
        说,内存释放释放的只能是数据段的内容(包括堆和栈,但释放栈上的内存由系统
        进行),而代码段的内存在内存中是永远不会释放/改变的,直到程序结束。因此
        在内存释放后也是可以访问的。所以,一般所谓的释放内存delete操作,是在数据
        段进行的释放。但是,并不要因为这样,就违背上面的原则。

    ******************************************************************************/


    /*-----------* 下面是 new 应用实例 *----------------------------------------------*/
    /*
        F1 中的 new operator,他的行为就像F2中的 operator new 和 placement new 一样,
        也可以用F3中的方法达到相同的效果。
    */

    #include <new>
    #include <stdio.h>

    class CTest
    {
    public:
        CTest(int _x, int _y)
        {
            X = _x;
            Y = _y;
        }
        ~CTest()
        {
            X = 0;
            Y = 0;
        }
        void Test(char* sz)
        {
            printf("%s: X=%d Y=%d /n", sz, X, Y);
        }
        int X;
        int Y;
    };

    /* 
        new operator:
    */
    void F1()
    {
        CTest* pT = new CTest(1, 1);             // new operator
        
        pT->Test("F1");

        delete pT;

    }

    /*
        operator new
        placement new
    */
    void F2()
    {
        void*  p  = operator new(sizeof(CTest)); // operator new : 分配内存
        CTest* pT = new(p) CTest(2, 2);          // placement new: 构造对象
        
        pT->Test("F2");
        
        pT->~CTest();                            // 必须显示析构对象
        operator delete(pT);                     // operator delete: 释放内存
    }

    /*
        也可这样实现:
    */
    void F3()
    {
        char*  p  = new char[sizeof(CTest)]; // new operator: char为内置类型,不会调用构造函数,相当于只分配内存

        CTest* pT = new(p) CTest(3, 3);      // placement new: 在这块内存上构造CTest对象
        
        pT->Test("F3");
        
        pT->~CTest();                        // 必须显示析构CTest对象

        delete [] p;                         // delete operator: char为内置类型,不会调用析构函数,相当于只释放内存
    }


    void main()
    {
        F1();
        F2();
        F3();
    }

  • 相关阅读:
    课后作业07--二分法查找算法代码
    检索03 -- 二分法查找
    课堂随笔05--冒泡排序
    课后作业 06 -- 小数后几位精确判断并输出
    课后作业 05 -- 输出一段带字母与数字的随机数
    课后作业 04 --DateTime应用,判断多久后生日之类
    检索02--随机数种子的一些概念和理解
    课堂随笔04--关于string类的一些基本操作
    P1174 互素
    P1001 第K极值
  • 原文地址:https://www.cnblogs.com/dsky/p/2547807.html
Copyright © 2011-2022 走看看