zoukankan      html  css  js  c++  java
  • new/delete malloc/free深入剖析

    malloc和free是C语言用来分配和回收堆内存的函数,而new和delete是C++语言的引进的关键字。

    -- malloc函数

    void *malloc( size_t size );
    

    该函数需要传入一个参数,该参数指明要分配多少个字节的内存;返回一个void类型的指针。

    示例用法:int* a = (int*)malloc(4*sizeof(int));

    -- free函数

    void free( void *memblock );
    

    该函数需要传入待回收堆内存的首地址。

    关于C语言的内存分配和释放,比较简单,没什么需要特别注意的地方。

    ----------------------------------------------------------------------------

    以下重点讨论new/delete,new [] / delete[]的一些要注意的细节。

    对于基本类型而言,new只会为其分配相应的内存;delete只会将分配的内存进行回收。

    但对于复合类型,new除了分配内存之外,还会调用该对象的构造函数来对对象进行初始化;delete除了回收内存,还会在此之前,调用该对象的析构函数来对对象执行清理工作。

     

    语言内建的new/delete,new [] / delete[]的实现

    class CPoint
    {
    public:
        CPoint()
        {
            m_X = 0;
            m_Y = 0;
        }
        CPoint(double x, double y)
        {
            m_X = x;
            m_Y = y;
        }
    
        virtual ~CPoint() 
        {
    
        }
    private:
        double m_X, m_Y;
    };
    
    //////////////////////////////////////////////////////////
    CPoint* p1 = new CPoint(2.5, 3.5);
    delete p1;
    
    CPoint* p2 = new CPoint[5];
    delete [] p2;

    实现代码如下:

    // new的实现
    CPoint* p1 = nullptr;
    try
    {
    	void* mem = operator new(sizeof(CPoint)); // 申请内存
    	p1 = static_cast<CPoint*>(mem);
    	p1->CPoint::CPoint(2.5, 3.5);  // 调用构造函数
    }
    catch (std::bad_alloc& e)
    {
    }
    
    // delete的实现
    p1->CPoint::~CPoint();  // 调用析构函数
    operator delete(p1);  // 释放内存
    
    // new[]的实现
    CPoint* p2 = nullptr;
    try
    {
    	void* mem = operator new(sizeof(CPoint)*5);  // 申请内存
    	p2 = static_cast<CPoint*>(mem);
    	for (int i=0; i<5; ++i)
    	{
    		(p2+i)->CPoint::CPoint();  // 逐个调用构造函数
    	}
    }
    catch (std::bad_alloc& e)
    {
    }
    
    // delete[]的实现
    for (int i = 0; i < 5; ++i)
    {
    	(p2 + i)->CPoint::~CPoint();  // 逐个调用析构函数
    }
    operator delete(p2);  // 释放内存
    

    重载operator new / operator delete,operator new [] / operator delete[]

    operator new new[]类似于malloc,只分配堆内存,不调用相关对象的构造函数

    operator delete delete[]类似于free,只释放堆内存,不调用相关对象的析构函数

    (1)全局重载

    //一般的版本
    void* operator new(std::size_t count) throw(std::bad_alloc)
    {
        return malloc(count);
    }
    void* operator new[](std::size_t count) throw(std::bad_alloc)
    {
        return malloc(count);
    }
    void* operator new(std::size_t count, void *ptr) // placement new
    {
        return (ptr);
    }
    
    void* operator new[](std::size_t count, void *ptr) // placement new
    {
        return (ptr);
    }
    
    
    void operator delete(void* p) throw()
    {
        free(p);
    }
    void operator delete[](void* p) throw()
    {
        free(p);
    }
    //兼容早版本的new,内存分配失败不会抛出异常
    void* operator new(std::size_t count, const std::nothrow_t&) throw();
    {
        return malloc(count);
    }
    void* operator new[](std::size_t count, const std::nothrow_t&) throw()
    {
        return malloc(count);
    }
    
    void operator delete(void* p, const std::nothrow_t&) throw()
    {
        free(p);
    }
    void operator delete[](void* p, const std::nothrow_t&) throw()
    {
        free(p);
    }

    C++17还支持带对齐参数的operator new

    (2)类内重载

    class CTest
    {
    public:
    	//一般的版本
    	void* operator new(std::size_t count) throw(std::bad_alloc)
    	{
    		return malloc(count);
    	}
    	void* operator new[](std::size_t count) throw(std::bad_alloc)
    	{
    		return malloc(count);
    	}
    	void* operator new(size_t, void *_Where) // placement new
    	{
    		return (_Where);
    	}
    	void operator delete(void* p) throw()
    	{
    		free(p);
    	}
    	void operator delete[](void* p) throw()
    	{
    		free(p);
    	}
    	//兼容早版本的new,内存分配失败不会抛出异常
    	void* operator new(std::size_t count, const std::nothrow_t&) throw()
    	{
    		return malloc(count);
    	}
    	void* operator new[](std::size_t count, const std::nothrow_t&) throw()
    	{
    		return malloc(count);
    	}
    	void operator delete(void* p, const std::nothrow_t&) throw()
    	{
    		free(p);
    	}
    	void operator delete[](void* p, const std::nothrow_t&) throw()
    	{
    		free(p);
    	}
    	
    //static版本
    //public:
    //	static void* operator new(std::size_t count) throw(std::bad_alloc)
    //	{
    //		return malloc(count);
    //	}
    //	static void* operator new[](std::size_t count) throw(std::bad_alloc)
    //	{
    //		return malloc(count);
    //	}
    //	static void* operator new(size_t, void *_Where) // placement new
    //	{
    //		return (_Where);
    //	}
    //	static void operator delete(void* p) throw()
    //	{
    //		free(p);
    //	}
    //	static void operator delete[](void* p) throw()
    //	{
    //		free(p);
    //	}
    //	//兼容早版本的new,内存分配失败不会抛出异常
    //	static void* operator new(std::size_t count, const std::nothrow_t&) throw()
    //	{
    //		return malloc(count);
    //	}
    //	static void* operator new[](std::size_t count, const std::nothrow_t&) throw()
    //	{
    //		return malloc(count);
    //	}
    //	static void operator delete(void* p, const std::nothrow_t&) throw()
    //	{
    //		free(p);
    //	}
    //	static void operator delete[](void* p, const std::nothrow_t&) throw()
    //	{
    //		free(p);
    //	}
    };

    注1:operator new/delete,operator new [] / delete[]被重载后,程序员需要在其中编写内存申请和内存释放的逻辑

    注2:对于复合类型,执行new操作时,按照以下顺序来确定new进行内存申请,然后调用构造函数

             ①类内new重载   ②自定义全局new重载   ③语言内建new

    注3:对于基本类型,执行new操作时,按照以下顺序来确定new进行内存申请

             ①自定义全局new重载  ②语言内建new

    注4:对于复合类型,执行delete操作时,会先调用析构函数,最后按照以下顺序来确定delete进行内存释放

             ①类内delete重载   ②自定义全局delete重载   ③语言内建delete

    注5:对于基本类型,执行delete操作时,按照以下顺序来确定delete进行内存释放

             ①自定义全局delete重载  ②语言内建delete

    注6:对于复合类型,使用::new和::delete来调用,按照以下顺序来确定全局new/delete

             ①自定义全局new/delete重载  ②语言内建new/delete

    注7:另外,可以把重载后的new/delete当作普通函数一样调用;需要注意的是,此时不会调用构造函数和析构函数,程序员需要自己显示地调用

    // operator
    CTest* p1 = (CTest*)operator new(sizeof(CTest));  // 调用全局new操作符重载
    p1->CTest::CTest(); // 需要显示调用构造函数
    
    p1->~CTest(); // 需要显示调用析构函数
    operator delete(p1); // 调用全局delete操作符重载
    
    // ::operator
    CTest* p2 = (CTest*)::operator new(sizeof(CTest));  // 调用全局new操作符重载
    p2->CTest::CTest(); // 需要显示调用构造函数
    
    p2->~CTest(); // 需要显示调用析构函数
    ::operator delete(p2); // 调用全局delete操作符重载
    
    // CTest::operator
    CTest* p3 = (CTest*)CTest::operator new(sizeof(CTest));  // 调用类的new操作符重载
    p3->CTest::CTest(); // 需要显示调用构造函数
    
    p3->~CTest(); // 需要显示调用析构函数
    CTest::operator delete(p3); // 调用类的delete操作符重载
    

    placement new

    有时可能会有一些分配好的原始内存,我们需要在上面构建对象。placement new可以用来达到此目的。

    语言內建的placement new是c++标准程序库的一部分,要使用placement new,必须#include <new>。placement new在已有的内存地址上构建对象,并会调用构造函数。

    使用完这个对象后,需要显示调用它的析构函数来做对象的清理工作。

    placement new创建的对象不能直接delete进行销毁,至于对象所占的内存如何处理,要看这块内存的具体来源。

    placement new的重载及覆盖规则与普通的new是一样的,参见上文。

    具体用法:

    class widget
    {
    public:
        widget(int n);
    };
    
    widget* constructWidgetInBuf(void* buf, int n)
    {
        return new (buf) widget(n);
    }
    

    *** new的异常处理 ***

    如果new了很大的一块空间,最好是进行异常判断。

    try{
        int* pMem = new int[100000];
    }
    catch(std::bad_alloc& e) // 需要#include <new>
    {
        std::cout << e.what() << std::endl;
    }
    
  • 相关阅读:
    c# 键值数据保存XML文件
    c# 封装 Request操作类
    c# 获取客户端IP
    c#封装DBHelper类
    c# Cache 使用实例
    c#cookie读取写入操作
    c# Session写入读取操作
    ABAP-HTTP支持
    WDA-文档-基础篇/进阶篇/讨论篇
    UI5-文档-4.38-Accessibility
  • 原文地址:https://www.cnblogs.com/kekec/p/2116498.html
Copyright © 2011-2022 走看看