欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

内存管理(一)

程序员文章站 2022-04-26 12:10:36
...

当我们new了一个数组,但是我们delete 的时候却没加 [] ,此时会发生内存泄露???
这个先说明, 这个不一定会发生内存泄露.

#include <iostream>

class A
{
public:
	A(){};
private:
	int i;
};

当我们申请一个数组时,如果这个类中没有涉及到动态分配过的内存,或者说这个类的析构函数没有写的必要(如 A 类 ).
此时当我们执行 A* apoint = new A[10]; 这条语句后,分配的内存空间图如下:
内存管理(一)
这个 cokkie 字段就记录了我们申请了 几个 空间,这里cookie 中就记录了 10 个元素.当我们delete时,即使不加 [],free 会向*问,访问到cookie,它就会直到free几个元素,所以不会造成内存泄露

但是当我们的类中有动态分配的空间,如 string 类.
string* p = new string[3];

这里如果delete 的时候没有加 [] ,则只会调用 一次 析构函数,另外两个空间不会释放,造成内存泄露.

上面的测试前提是不写析构函数,如果写了析构函数,那么内存块的布局就不同了内存管理(一)
如果写了析构函数,那么cookie 下面就会多一个字段,表明申请了几个元素,此时,free()向*问,访问的就不是cookie,就会报错.

new 的具体行为

编译器执行这一行时, complex *pc = new complex(1,2); 会转化为

complex *pc; 
try
{
	void* mem = operator new(sizeof(complex)); //分配内存
	pc = static_cast<complex*>(mem);  //转化类型
	pc->complex::complex(1,2); //调用构造函数
	//注意这里调用的构造函数有点特殊,只有编译器可以通过类名,直接调用构造函数
}

catch(std::bad_alloc)
{
//抛出异常
}

我们再找到operator new 的源代码可以发现, operator new 里面也是调用了 malloc .

void* __CRTDECL operator new(size_t const size)
{
    for (;;)
    {
        if (void* const block = malloc(size))
        {
            return block;
        }

        if (_callnewh(size) == 0)
        {
            if (size == SIZE_MAX)
            {
                __scrt_throw_std_bad_array_new_length();
            }
            else
            {
                __scrt_throw_std_bad_alloc();
            }
        }

        // The new handler was successful; try to allocate again...
    }
}

当内存分配成功时,就return block,否则就是个死循环.

这里 解释 一下 _callnewh.
很明显,这里是分配内存失败(内存不足)才进入这个函数,进入这个函数释放内存,然后继续尝试分配.

这个c++ 提供了一个函数可以设置 --set_new_handler();

在分配内存错误之前,系统会抛出一个异常,但是在抛出异常之前,会重复调用_callnewh()

以上结论下面皆有测试.

测试如下:

#include <iostream>
#include <new>
#include <cassert>

using namespace std;

void noMoreMemory()
{
    //    cerr << "out of memory" << endl;                                  1
//      abort();                                                                                        2
}


int main()
{
//      set_new_handler(noMoreMemory);                                     3
   
        int *p = new int[100000000000000000];

        assert(p);
 
        return 0;
}

测试一: 注释 3
结果:
内存管理(一)
测试结果表明 : 系统抛出异常
测试二: 注释 2
结果:
内存管理(一)
测试结果表明: 出现了死循环,系统在不断的调用我们set_new_handler()这个函数

测试三: 都不注释
内存管理(一)
测试结果表明: 调用abort();退出循环

接着上面谈:
编译器将这行代码 delete pc;
转化为:

pc->~complex(); //调用析构函数
operator delete(pc);  //释放内存
void operator delete( void * p )  
{  
    RTCCALLBACK(_RTC_Free_hook, (p, 0));  
  
    free( p );  
}  

从源码可以得出-- delete -> operator delete -> free

相关标签: 内存管理