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

    对于计算机程序设计而言,变量和对象在内存中的分配都是编译器在编译程序时安排好的,这带来了极大的不便,如数组必须大开小用,指针必须指向一个已经存在的变量或对象。对于不能确定需要占用多少内存的情况,动态内存分配解决了这个问题。

    一、new/delete 简介


    new 和 delete 是 C++ 用于管理 堆内存 的两个运算符,对应于 C 语言中的 malloc 和 free,但是 malloc 和 free 是函数,new 和 delete 是运算符。除此之外,

    new 在申请内存的同时,还会调用对象的构造函数,而 malloc 只会申请内存;同样,delete 在释放内存之前,会调用对象的析构函数,而 free 只会释放内存。

    new 运算符的内部实现分为两步:

    • 内存分配(先)

      调用相应的 operator new(size_t) 函数,动态分配内存。如果 operator new(size_t) 不能成功获得内存,则调用 new_handler() 函数用于处理new失败问题。如果没有设置 new_handler() 函数或者 new_handler() 未能分配足够内存,则抛出std::bad_alloc 异常。“new运算符”所调用的 operator new(size_t) 函数,按照C++的名字查找规则,首先做依赖于实参的名字查找(即ADL规则),在要申请内存的数据类型T的 内部(成员函数)、数据类型T定义处的命名空间查找;如果没有查找到,则直接调用全局的 ::operator new(size_t) 函数。

    • 构造函数(后)

    • 在分配到的动态内存块上 初始化 相应类型的对象(构造函数)并返回其首地址。如果调用构造函数初始化对象时抛出异常,则自动调用 operator delete(void*, void*) 函数释放已经分配到的内存。

    这是string *s = new string("a value"); 这句表达式内部的实现:

     我们可以看出new内部的调用顺序:(初始化一个对象时)

    new内部的调用顺序:(初始化若干个对象时)

     

     

    delete 运算符的内部实现分为两步:

    • 析构函数(先)

      调用相应类型的析构函数,处理类内部可能涉及的资源释放。

    • 内存释放(后)

      调用相应的 operator delete(void *) 函数。调用顺序参考上述 operator new(size_t) 函数(ADL规则)。

    同样地,delete对象时,调用顺序为:(delete单个对象时)

     

     

     

     

    delete对象时,调用顺序为:(delete多个对象时)

    二、new/delete 表达式语法


    2.1 new 表达式语法

    一、new用法

    1.开辟单变量地址空间

       使用new运算符时必须已知数据类型,new运算符会向系统堆区申请足够的存储空间,如果申请成功,就返回该内存块的首地址,如果申请不成功,则返回零值

        new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的,而动态创建的对象本身没有标识符名。

     一般使用格式:
            格式1:指针变量名=new 类型标识符;
            格式2:指针变量名=new 类型标识符(初始值);
            格式3:指针变量名=new 类型标识符 [内存单元个数];

    说明:格式1和格式2都是申请分配某一数据类型所占字节数的内存空间;

         格式2在内存分配成功后,同时将一初值存放到该内存单元中;

         格式3可同时分配若干个内存单元,相当于形成一个动态数组

        1)new int;  //开辟一个存放整数的存储空间,返回一个指向该存储空间的地址。int *a = new int 即为将一个int类型的地址赋值给整型指针a

        2)int *a = new int(5) 作用同上,但是同时将整数空间赋值为5

    比较经典的几个例子(结合指针来理解一下):

    int *p=new int;    (*p)才是指针

    int **p =new int *;

    int **p=new int *[3];//指针数组

    int (*p)[3]=new int[2][3];//int类型的二维数组(),我们分析正常的二维数组可以知道,对于int a[2][3],可以看作int [3] a[2],即有一个一维数组,里面有两个元素,每个元素的类型都是int [3]

    int  * (*p)[3]=new int *[2][3];//int指针类型的二维数组

    node *p=new node[2];//结构体数组

    2.开辟数组空间

        对于数组进行动态分配的格式为:  `      

           delete a;   //释放单个int的空间

    2. 删除数组空间

           int *a = new int[5];

           delete []a;    //释放int数组空间

    记住:new和delete就像malloc和free一样,都要成对使用哦。

    三、new/delete 和malloc/free的区别和联系


    2.1 new 表达式语法

    一、new用法

    new和delete与malloc和free一样,都是存在堆上的。那么,二者有什么异同呢?

    总结new/delete和malloc/free的区别和联系:

    1. 它们都是动态管理内存的入口。

    2. malloc/free是C/C++标准库的函数,new/delete是C++操作符。

    3. malloc/free只是对内存进行分配和释放;new/delete还负责完成了创建和销毁对象的任务。

    4.new的安全性要高一些,因为他返回的就是一个所创建的对象的指针,对于malloc来说返回的则是void*,需要手动计算类型大小,进行强制类型转换,显然这是一个危险的漏洞

    5.new/delete的底层调用了malloc/free。

    6.malloc/free申请空间后得判空,new/delete则不需要。

    7.new直接跟类型,malloc跟字节数个数。

    8.我们可以对new/delete重载,使内存分配按照我们的意愿进行,这样更具有灵活性,malloc则不行。

    不过,new/delete也并不是十分完美,大概最大的缺点就是效率低(针对的是缺省的分配器),原因不只是因为在自由存储区上分配(和栈上对比),而且new只是对于堆分配器(malloc/realloc/free)的一个浅层包装,没有针对小型的内存分配做优化。另外缺省分配器具有通用性,它管理的是一块内存池,这样的管理往往需要消耗一些额外空间。我们可以针对new/delete进行重写以追求更高的效率,对于这方面更深入的探讨可以参考《Effective C++》第八章。

    四.使用时的注意事项


    1. new 和delete都是内建的操作符,语言本身所固定了,无法重新定制,想要定制new和delete的行为,徒劳无功的行为。

    2. 动态分配失败,则返回一个空指针(NULL),表示发生了异常,堆资源不足,分配失败。

    3. 指针删除与堆空间释放。删除一个指针p(delete p;)实际意思是删除了p所指的目标(变量或对象等),释放了它所占的堆空间,而不是删除p本身(指针p本身并没有撤销,它自己仍然存在,该指针所占内存空间并未释放),释放堆空间后,p成了空指针。

    4. 内存泄漏(memory leak)和重复释放。new与delete 是配对使用的, delete只能释放堆空间。如果new返回的指针值丢失,则所分配的堆空间无法回收,称内存泄漏,同一空间重复释放也是危险的,因为该空间可能已另分配,所以必须妥善保存new返回的指针,以保证不发生内存泄漏,也必须保证不会重复释放堆内存空间。

    5. 动态分配的变量或对象的生命期。我们也称堆空间为自由空间(free store),但必须记住释放该对象所占堆空间,并只能释放一次,在函数内建立,而在函数外释放,往往会出错。

    6. 要访问new所开辟的结构体空间,无法直接通过变量名进行,只能通过赋值的指针进行访问。

        用new和delete可以动态开辟和撤销地址空间。在编程序时,若用完一个变量(一般是暂时存储的数据),下次需要再用,但却又想省去重新初始化的功夫,可以在每次开始使用时开辟一个空间,在用完后撤销它。



    参考资料:

    http://blog.csdn.net/hihozoo/article/details/51441521(placement new 运算符,在栈上new对象可参考)

    http://blog.csdn.net/xxpresent/article/details/53024555

    http://blog.csdn.net/dreamiond/article/details/75201473

    https://www.cnblogs.com/wanqieddy/p/4372033.html

  • 相关阅读:
    Linux之开源软件移植
    数学问题的解题方法(模板)
    图论相关算法理解和总结
    关于ACM,关于CSU
    hdu 4607(树的直径)
    树的直径
    SGU-181 X-Sequence
    1629 B君的圆锥
    1134 最长递增子序列(暴力写的)
    1483 化学变换(暴力)
  • 原文地址:https://www.cnblogs.com/curo0119/p/8526092.html
Copyright © 2011-2022 走看看