• new、delete和malloc、free的一点思考


    一、new与malloc,free与delete的异同

    (1)相同点

    都用于动态内存的申请、释放

    (2)不同点

    • new与delete是C++的运算符,支持运算符重载,而malloc与free是C/C++的标准库函数,支持函数覆盖;
    • new能自动计算所需内存大小,malloc需要传入分配内存大小;
    • new申请成功返回的是指向该类型的指针,而malloc申请成功返回的void *的指针,所以我们一般调用malloc的形式为(int *)malloc(10*sizeof(int));
    • new和delete操作有两步,先调用operator new的标准库函数(里面包含了malloc),调用构造函数(如果有必要),delete反之,所以new、delete底层调用了malloc、free标准库函数;
    • new之后采用free语法上没有操作,大多时间也不会报错,但是会有意外情况,new基本数据类型与travial destructor的数组,这种情况free不报错,其他情况会报段错误(这里在第二节会详细测试);
    • delete调用一次析构函数、delete[]调用多个析构函数,取决于申请的空间的前4个字节(存放delete[] 调用析构函数的次数);
    • new、delete不需要库文件的支持,而后者需要库文件的支持;
    • new有三种方式,plain new,nothrow new,placement new;

    (3)free为什么不需要指定大小

    系统在分配内存时除了分配指定的内存空间外,还要分配用于保存内存空间大小等信息。所以内存释放时不再需要再指定释放多大的内存空间,只需要指定该块内存空间的首地址即可。

    实质上,能在pp指针前12个字节标记出申请内存的大小,是依靠着struct malloc_chunk这个结构体。

    具体测试

     在红框的位置,出现的是申请内存的大小(经过了多次测试)。

    二、new、delete、malloc、free的一些测试

    之前使用new和delete时发现的一些有趣事

     1 typedef char* p_char;
     2 #define d_char char *
     3 class T {
     4 public:
     5     int a;
     6     T() { a = 10; cout << "constructor" << endl; }
     7     ~T() {
     8         cout << "destructor" << endl; 
     9     }
    10 };
    11 
    12 int main()
    13 {
    14     const int NUM = 3;
    15     T* p1 = new T[NUM];
    16     cout << hex << p1 << endl;      //输出P1的地址
    17     delete p1;
    18 }

    这是我写的错误代码,一开始认为只会是内存泄漏,但是实际情况是段错误,输出为

    以下是我的进行的思考

    (1)malloc、free的测试

    根据上述图片出现的情况,实际输出了一次析构函数后出现段错误,我将问题定位在delete的第二步,operator delete时出现错误,测试以下代码。

    1 int* pp = (int *)malloc(10 * sizeof(int));
    2     pp++;
    3     free(pp);

    发现也会报段错误;

    推断free只能从申请的首地址进行释放内存,应该是和操作系统内存管理有关系,比如free是从free list中读取的内存信息,才能准确释放内存,所以上述代码出现段错误;

    所以delete一个new的数组,从p1的位置调用析构函数没有错误,但是free时,没有找到正确的申请内存首地址(应该是p1-4),所以free失败,产生段错误;

    (2)new、delete测试

    这样测试完,后我将T类变成基本数据类型,发现没有异常;

    1  int * a = new int[10];
    2      delete a;

    查阅、咨询别人,得出结论是,系统申请基本数据类型,并不会多申请4个字节的内存所以不会报段错误,多申请4个字节是为了记录class调用的析构函数次数;

    然后又测试了以下代码

     1 class T {
     2 public:
     3     int a;
     4     T() { a = 10; cout << "constructor" << endl; }
     5     //~T() {
     6     //    cout << "destructor" << endl; 
     7     //}
     8 };
     9 
    10 int main()
    11 {
    12     const int NUM = 3;
    13     T* p1 = new T[NUM];
    14     cout << hex << p1 << endl;      //输出P1的地址
    15     delete p1;
    16 }

    惊奇的发现,也并没有报段错误;

    查阅资料后,推断travial destructor的类应该不会调用析构函数,或者说被编译器优化掉了;

    (3)得出结论

    • new一个类型的数据,如果不是基本数据类型、travial destructor,会多申请4个字节的内存大小,存放调用析构函数的次数;
    • malloc会在操作系统某一个位置记录申请内存大小,所以free才能准确的释放内存;
    • delete[]存在会使编译器获取数组大小(size)然后析构函数再被依次应用在每个元素上,一共size次。否则,只有一个元素被析构,无论那种情况,都会将new的内存全部返还给操作系统

    最后感谢以下大佬的博客

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

    https://www.cnblogs.com/hezhixiong/p/4535534.html

    感谢阿秀大佬

    https://gitee.com/Rosasp/CPlusPlusThings

  • 相关阅读:
    如何实现ZBrush 4R7中按钮颜色的自定义
    Zbrush遮罩边界该怎么实现羽化和锐化
    怎么在ZBrush中通过遮罩得到子物体
    怎样用好ZBrush中的PaintStop插件
    SQL 如果存在就更新,如果不存在就添加,使用 Merge 函数(SQL2008版本及以上)
    SQL 实现,如果存在就更新,如果不存在就添加
    验证码,字体旋转。
    瀑布流代码,简洁版 带分页
    瀑布流代码,简洁版
    EF
  • 原文地址:https://www.cnblogs.com/cutelife/p/14803675.html
Copyright © 2020-2023  润新知