C++:new与delete

标题

    • C++内存管理
    • new、delete的底层实现
      • new单个空间:
      • delete单个空间
      • new多个空间
      • delete多个空间
    • 定位new

C++内存管理

从堆中申请空间,new和delete不是函数,而是关键字或称为运算符

C++:new与delete_第1张图片
与C语言中动态申请空间的不同:

  1. 不需要强制类型转化
  2. 不需要计算开辟空间所需的字节数
  3. 对new出的空间不需要判空
  4. 在申请空间的同时可以直接初始化

malloc,free;new,delete在使用时要匹配

1.现有一个test类,类中涉及对资源的管理

class test
{	
private:
	int _data;
	int* _p;
public:
	test( )
	{	
		_p=new int[10];
		cout<<test( )<<endl;
	}
	~test( )
	{	
		delete _p[ ];
		cout<<~test( )<<endl;
	}
};

动态开辟内存,调用不匹配的后果:(动态申请内置类型的空间,释放时free和delete可以混用,对于自定义类型不能)

test* p1=(test*) malloc (sizeof(test));
delete p1;

  • 我们希望使用malloc函数申请一个test类型的对象,malloc确实申请空间成功,但未打印出构造函数中的test(),所以p1指向的空间不是对象,而是指向与test类型大小相同的空间;
  • 用delete释放空间会调用析构函数(将p1指向的空间当做对象),此时会报错,因为在malloc申请空间不会调用构造函数

test* p2=new test
free(p2); 调用了构造函数但是没有调用析构,产生内存泄漏
test* p3=new test
delete[] p3;直接中断


test* p4=new test[10];
free(p4);未调用析构内存泄漏
test* p5=new test[10]
delete p5;直接中断

综上所述:

  • new不但申请空间成功,还调用了构造函数;
  • delete不但释放空间成功,在释放前还调用了析构函数
  • malloc和free不会调用构造和析构

new T,T是内置类型----申请方式与malloc相似

  1. 申请空间;
  2. 调用构造函数对空间内容初始化

现有test类,类中不涉及对资源的管理

class test
{	
private:
	int _data;
public:
	test( )
	{	
		cout<<test( )<<endl;
	}
	
	~test( )
	{
		cout<<~test( )<<endl;
	}
};

test* p1=new test;
delete(p1);
test* p2=new test[10];
delete(p2);

p1和p2对象均可开辟成功,但是在delete时出错;如果将析构函数屏蔽,delete时可以通过编译,也不会产生内存泄漏;为何如此需要我们了解new和delete的底层实现;

new、delete的底层实现

  • new和delete是用户进行动态内存申请和释放的操作符,
  • operator new 和operator delete是系统提供的全局函数,
  • new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:C++:new与delete_第2张图片

new单个空间:

int* p1=new T; 的底层执行顺序:

  1. 调用operator new函数,申请一个T类型空间
  2. 调用operator new函数成功后调用构造

operator new:

  1. 将T作为参数传给该函数;(所以函数不需要进行类型转化,即知道了参数类型,又知道了所需要开辟空间的大小)
  2. 进入while循环,用malloc申请空间;
    申请成功退出循环,失败(空间不足才失败)则进入循环体执行if语句;
  3. if语句是在申请空间失败时,用_callnewh函数指针,调用其指向的函数寻找空间;(指向的函数需要由用户定义)
    p=_callnewh(size)返回值如果不为0,则表明判断体内的函数找到了空间,直接返回新空间地址,重复申请操作;
    内部语句在找不到空间时抛异常
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
	void *p;
 	while ((p = malloc(size)) == 0)
 	if (_callnewh(size) == 0)
 	{
 	// report no memory
 	// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
 		static const std::bad_alloc nomem;
 		_RAISE(nomem);
 	}
 	return (p);
}

delete单个空间

释放自定义类型空间:
delete p2;:底层执行顺序

  • delete内部调用T::scalar deleting destructor函数; 在该函数内部:
    先调用析构函数,释放对象中的资源;
    再调用 operator delete函数释放对象本身的空间

operator delete

void operator delete(void *pUserData)
{
	_CrtMemBlockHeader * pHead;
 	RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
 	
 	if (pUserData == NULL)
 	//对用户传进来的地址进行判空;为空直接返回,不等于空开始下列凑字
 		return;
 		
 	_mlock(_HEAP_LOCK);
 	__TRY
 		pHead = pHdr(pUserData);
 		_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
 		_free_dbg( pUserData, pHead->nBlockUse );
 		//free释放空间
 	__FINALLY
 	_munlock(_HEAP_LOCK); 
 	__END_TRY_FINALLY
 return;
}

operator delete函数释放对象本身的空间,本质是通过free释放;只需了解注释部分

new多个空间

int*p1=new T[N];的底层执行顺序:

  1. 申请空间调用operator new[]函数,该函数内部调用operator new(size),然后循环调用malloc创建空间;
  2. 如果是自定义类型,调用构造初始化;
  3. 如果是自定义类型对象,调用N次构造函数初始化空间
  • 如果是自定义类型对象,类中有析构会多传4个字节,这四个字节保存的是对象个数,编译器需要调析构函数的次数
  • 没有析构函数则不会传

delete多个空间

1.清理N个对象中的资源,调用N次析构函数
2.释放空间
调用operator delete[]函数,
内部调用operator delete来释放空间

定位new

定位new:在已经申请好的空间上执行构造函数,把该空间变成对象

malloc动态申请的空间需要被操作系统管理:

  • 空间上方有一个结构体,对所申请的空间进行管理,
  • 空间下方有4个字节的结尾标记,防止使用越界
  • 多次使用malloc就会浪费很多空间,这些空间被用于每个动态空间的管理,所以就一次性申请很大的空间,但是这些空间会用于各种类型,
  • 如何将这些空间当类型来使用该如何处理?

所以需要在已申请好的空间上执行构造函数,将其变成对象
C++:new与delete_第3张图片定位new底层调用:这样做是为了和new调用过程一致

  1. 先调用void* operator new(size_t, void*)函数(两个参数),
    该函数是对operator new的重载(便于编译器处理两个new重载);
    内部只返回p的空间
  2. 再调用构造函数
p->test(); //p此时是对象,可直接以调用析构函数,因为p已经是对象
free(p);   //这两步就等于delete p

-相当于delete p;

你可能感兴趣的:(C++,c++)