C++动态内存管理( 深度解析·new-delete-new[]-delete[] )

整体代码测试环境:VS 08
C中的动态内存管理方式

                   1.堆上申请内存空间                    

(1)malloc申请 内存空间
void* malloc(字节数)
 说明:这个函数向内存申请⼀一块连续可⽤用的空间,并返回指向这块空间的指针。  

    - 如果开辟成功,则返回⼀一个指向开辟好空间的指针。 

    - 如果开辟失败,则返回⼀一个NULL指针,因此malloc的返回值⼀一定要做检查。 

    - 返回值的类型是 void* ,所以malloc函数并不不知道开辟空间的类型,具体在使⽤用的时候使⽤用者 ⾃自⼰己来决定。 - 如果参数 size 为0,malloc的⾏行行为是标准是未定义的,取决于编译器

(2)calloc申请内存空间

void* calloc(元素个数,元素类型字节)-->0 初始化
说明:函数的功能是为 num 个⼤大⼩小为 size 的元素开辟⼀一块空间,并且把空间的每个字节初始化为 0。 

   与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为 全0

(3)realloc申请内存空间
void* realloc(void* p, size_t size)
  说明:ptr 是要调整的内存地址  

   size 调整之后新⼤大⼩小 返回值为调整之后的内存起始位置。 

   这个函数调整原内存空间⼤大⼩小的基础上,还会将原来内存中的数据移动到 新 的空间。 

   realloc在调整内存空间的是存在两种情况: 


注意:他们三个都需要free来释放,否则会发生内存泄漏问题


                   2.栈上申请内存空间                    

使用_alloca在栈上动态开辟内存,栈上开辟的内存由编译器自动维护,不需要用户显示释放

C++的动态内存管理方式
1.C++中通过new和delete运算符来进行动态内存管理

 


图 new和delete的使用方法

注:new申请出来的空间无需判空

      new[]和delete[]一定要搭配使用,否则可能出现内存泄漏甚至崩溃的问题

class Test
{
public:
	Test()
	{
		//_p = new int(10);
		cout<<"Test():"<<this<<endl;
	}
	~Test()
	{
		/*if(_p)
		{
			delete _p;
		}*/
		cout<<"~Test()"<<this<<endl;
	}

	int data;
	//int* _p;
};



void FunTestc()
{
	Test *p1 = (Test*)malloc(sizeof(Test)); //没有调用构造函数,不是一个完整的Test类型对象,_p指向的空间是随机值
	Test *p2 = (Test*)malloc(sizeof(Test));//屏蔽_p
	delete p1;
	delete[] p2;

	Test *p3 = new Test;
	Test *p4 = new Test;
	free(p3);
	delete[] p4;

	Test *p5 = new Test[10];
	Test *p6 = new Test[10];
	free(p5);
	delete p6;
}

int main()
{
	FunTestc();
	return 0;
}
一经调试,会发现走到  delete[] p2  的位置发生了中断,把  delete[] p2  屏蔽掉后,再往下走没有任何问题,但是一走到 delete[] p4 的位置就又发生了中断 ,具体原因我们在下面进行剖析


但是如果在类里面加入 int* _p和析构函数,就会发现在一开始的mian函数里面的 p1就发生了错误,原因是malloc在开辟的空间的时候,没有调用构造函数,也就是没有完成对对象的赋值,所以没有创建一个完整的Test对象,自然也就崩溃了

那么问题来了,既然C中有了malloc/free来管理内存空间,为什么C++还要定义new/delete运算符来动态管理内存空间?(面试题)

1.他们都是动态管理内存的入口

2.malloc/free是C/C++标准库的函数,而new/delete是C++的操作符

3.malloc/free只是动态分配内存空间/释放空间,而new/delete除了分配空间还会调用构造函数和析构函数进行初始化和清理(清理成员)

4.malloc/free需要手动的计算类型大小且返回值是void*,需要用户自定义其类型,new/delete可以自己计算类型的大小,返回对应的类型指针

2.深度剖析new/new[]/delete/delete[]
class Test
{
public:
	Test()
	{
		_data = 100;
		cout<<"Test():"<<this<<endl;
	}

	~Test()
	{	
		cout<<"~Test()"<<this<<endl;
	}

	int _data;
};

int main()
{
	Test* pA = new Test;
	delete pA;

	/*Test* pA = new Test[10];
	delete[] pA;*/
	return 0;
}

1.new

    我们在main函数的第一句代码处下一个断点,然后往里面一走,就会发现里面调用的是这样的:


其实就是将malloc重新封装了一下


总结一下new的机制:


2.delete


所以delete做的只有两件事:    调用pA对象指向的析构函数,对打开的文件进行关闭

                                               调用operator delete标准库函数来释放该对象的内存空间 ,传入函数的参数为                                                     pA 的值,也就是0x007d290   

3.new T[N]

经过调试发现new T[N]背后做了这些事



4.delete[]

那么delete[]就很简单了



猜你喜欢

转载自blog.csdn.net/ijn842/article/details/80970924