• malloc vs new && delete vs delete[]


    1.malloc vs new

    ①malloc分配的内存位于堆上,new分配的内存位于‘自由存储区’,自由存储区是C++中一个抽象的概念,有别于堆。一般的g++编译器实现的new的调用过程如下:new operator->operator new->malloc

    平时我们用的都是new operator,例如:int* p = new int,new operator会继续调用malloc(malloc不能被重载),所以一般情况下自由存储区就是在堆上的,但是operator new可以被重载,所以通过operator new分配的内存未必都在堆上,因此free store 和 heap还是有点区别

    ②malloc返回的指针类型是void*,需要手动强转为需要的类型,而new不需要如此,new的返回类型是类型安全的

    ③malloc分配内存失败会返回nullptr,而new则会直接抛出异常。另外new还可以通过set_new_handler设置分配失败时执行的逻辑,malloc则没有提供这样的使用方式

    ④在数组的内存分配上有所不同,malloc只管分配多大的字节,new []可以指定分配的是数组,并使用delete []释放

    ⑤new和delete会调用类的constructor和destructor,malloc则不会

    注:operator new是C++中的函数,内部调用malloc,返回类型为void*,只负责分配内存,而new operator是在C++编译层由编译器实现的,首先调用operator new分配内存,然后调用placement new调用对象的构造,如下可能是其实现的简单版本:

    new T;
    template<T>
    T* new_operator(args) {
       void* mem = ::operator new(sizeof(T));
       T* obj;
       if(BasicTypeTraits<T>::IsBasicType == false){
           obj = new (mem) T(args); //placement new  
       } else {
           obj = mem;
       }
       return obj;
    }
    

    其中args是类的构造函数的参数,可以不传,BaiscTypeTraits用来判断是否是基本类型,即C++ traits的应用,如果是基本类型无需调用构造函数,否则就要使用placement new调用对象的构造函数。

    这是单个类的情况,如果是new[] 就再加一个循环就OK了。

    delete  operator与之类似,首先调用对象的析构:obj->~T();然后再调用operator delete:operator delete(mem);

    另外operator new的简单实现如下:

    void * operator new(std::size_t size) throw(std::bad_alloc) {
        if (size == 0)
            size = 1;
        void* p;
        while ((p = ::malloc(size)) == 0) {
            std::new_handler nh = std::get_new_handler();
            if (nh)
                nh();
            else
                throw std::bad_alloc();
        }
        return p;
    }
    

    其中get_new_handler()获取set_new_handler设定的函数指针,如果没有设置就返回空。有趣的是,如果设置了set_new_handler,且传入的函数没有退出的话,可以发现程序将是一个死循环,直到malloc成功后才能结束循环。

    注:malloc分配的时候要指定分配内存的大小,利用free释放的时候却不需要指定大小,那么free是怎么知道该释放多大内存呢?在实现上,malloc分配的内存大小会比传入的值稍大,多余的内存用来存储分配的内存大小等额外的关于这段内存的信息(用户是无权访问的,应该是由操作系统管理),然后在free的时候找到这个内存的大小,就可以释放对应的内存了。另外因为delete内部实现也是调用free,因此delete也不用传入内存大小。

    2.delete vs delete[]

    在使用new/delete的时候,使用new分配内存要用delete释放,使用new[]分配要使用delete[]。那么二者的区别到底是什么呢?

    delete的过程是:先调用对象的析构函数,然后再调用free释放这个对象,delete[]同理。

    delete[]的出现主要是为了解决释放对象数组的时候调用其析构的问题。在使用new A[100]的时候显示的传入了创建对象的个数,因此很明确要调用多少次构造函数。但是在释放的时候delete[]还是没有传入对象的个数,那么其该如何知道调用多少次析构函数呢?

    可能的方式是这样的:因为delete在内部会调用free,而free能拿到内存的大小,delete的参数是个指针,可以获取指针的类型,就可以获得这个类型的大小,用总大小除以这个类型的大小就可以拿到数组中对象的个数。但是这样的前提是free先调用,在调用free之前不能拿到内存大小,而free一旦调用整个数组的内存就被释放了,这时候还没调用对象的析构呢,就可能导致内存泄漏了。那delete[]到底是怎么处理的呢?

    答案就是:在new A[100]的时候,这个数组长度100被存在了整个数组的前面size_t字节处(size_t根据平台不同可能是4或8),相当于new A[100]的时候分配的总内存是:malloc需要记录内存信息的空间+对象个数空间(size_t)+对象数组,然后返回地址是对象数组的首地址。在delete[]的时候先去拿到这个对象个数,再循环调用析构函数。前面说了malloc记录那个内存总大小用户无权访问,但是这个对象个数我们是可以拿到的,示例代码如下:

    #include<iostream>
    using namespace std;
    
    class A{
        public:
        A(){}
        ~A(){}
        int a;
        char b;
    };
    
    int main(){
    
    A* a = new A[6];
    size_t* p = (size_t*)a;
    p--;
    cout << *(size_t*)p <<endl;
    delete[] a;   //如果换成 delete a;程序可能崩溃
    a = NULL;
    
    int* aa = new int[100];
    delete aa; 
    return 0;  
    }
    

    最终的输出就是6

    如果delete[] a换成delete a,那么就只会调用一个对象的析构,然后就将数组的内存释放掉了,相当于其他5个对象的析构均没有调用,C++对这种情况的处理是未定义行为,运气好的话最多就是内存泄漏,运气不好程序就崩溃了,所以new[] delete[]必须成对的使用,而不能窜用。

    另外,如果new[]的是一个基本类型的数组,比如int,那么就不会将其长度记录,因为基本类型没有析构函数,直接调用free释放即可,所以上面示例程序的最后delete aa,是可以成功释放的,但是为了良好的编程习惯还是要写delete []。

    参考:

    https://stackoverflow.com/questions/240212/what-is-the-difference-between-new-delete-and-malloc-free

    https://stackoverflow.com/questions/1518711/how-does-free-know-how-much-to-free

    https://stackoverflow.com/questions/1350819/c-free-store-vs-heap

  • 相关阅读:
    【转】PHP使用共享内存进程间通信
    【转】php进程间通信--有名管道
    【转】代理模式-php白话示例
    [转]设计的核心任务之三:确保正交性
    pc主板支持独显和集显视频输出
    OpenGL核心技术之抗锯齿
    读书:有钱人想的和你不一样
    js 文本相似度
    js实现获得QQ截图或者微信截图后剪切板的内容clipboardData
    【转】chrome浏览器F12 Network中Timing参数含义
  • 原文地址:https://www.cnblogs.com/deepllz/p/9927807.html
Copyright © 2020-2023  润新知