c++内存管理:

目录

new和delete

使用方法:

注意事项:

new申请不需要检查返回值

operator new和operator delete函数的讲解


c语言申请内存有哪些方法:

答:malloc  calloc realloc三种

#include
void test()
{
	int*p1 = (int*)malloc(sizeof(int));
	free(p1);
	int*p2 = (int*)calloc(4, sizeof(int));
	int *p3 = (int*)realloc(p2, sizeof(int)* 10);
	free(p3);
}

其中,malloc就是普通的动态内存申请

calloc相当于malloc加上memset把申请的空间全部初始化为0

realloc相当于内存扩容,分为异地扩容和原地扩容,当扩容的次数少,空间小时,会执行原地扩容,当扩容的次数多,空间大时,会执行异地扩容。

原地扩容和异地扩容的区别?

答:如上图代码所示,p2和p3指针指向同一块空间,而异地扩容则不然,异地扩容会先找一块新的空间,然后把原空间的内容拷贝到新空间位置,然后释放掉原空间,所以返回的就是p3.

new和delete

使用方法:

c++是通过什么申请内存的呢?

答:c++是通过两个关键字(操作符)来申请和释放空间的。

new和delete

int main()
{
	int *p1 = new int;
	delete p1;
	return 0;
}

相当于这里申请一个字节的空间,返回指向该空间的指针p1,然后delete表示释放空间。

注意:这里申请空间并不会对空间上的内容完成初始化:

例如:

c++内存管理:_第1张图片

 我们申请的空间并没有进行初始化。

我们如何申请空间的同时并初始化呢?

答:我们可以这样操作

例如:

int main()
{
	int *p1 = new int(0);
	delete p1;
	return 0;
}

c++内存管理:_第2张图片

 我们在后面加上0表示申请空间并把空间初始化为0.

我们如何申请多个空间呢?

答:

int main()
{
	int *p1 = new int[10];
	delete[] p1;
	return 0;
}

这里表示我们要申请十个整型空间,注意:我们在delete释放时,要和我们申请的空间进行一一对应。

c++内存管理:_第3张图片

 假如我们要对申请的多个空间进行初始化呢?

答:我们可以这样写:

int main()
{
	int *p1 = new int[10]{1, 2, 3, 4};
	delete[] p1;
	return 0;
}

这里表示我们对申请的十个空间中的前4个进行初始化:c++内存管理:_第4张图片

 我们发现,对于内置类型,这里的new和delete和c语言的动态内存申请函数本质上没什么区别,那为什么++要定义这两个关键字呢?

答:对于内置类型,c语言和c++的动态内存申请本质是一样的,但是对于自定义类型,结果就不同了

例如:

我们写一个简单的类:

class A
{
	A(int a = 0)
	:_a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};

这个类中有两个成员函数,分别是构造函数和析构函数,函数的目的是当调用构造和析构函数,打印对应的提示并打印出this指针。

我们进行实验:

int main()
{
	/*int *p1 = new int[10]{1, 2, 3, 4};
	delete[] p1;
	return 0;*/
	A* p1 = new A;
	
}

我们动态内存申请一个类的空间,返回指向该空间的指针p1

我们进行编译:

 这里表示我们的动态内存申请new调用了构造函数。

但是我们的malloc是不会调用构造函数的:

int main()
{
	/*int *p1 = new int[10]{1, 2, 3, 4};
	delete[] p1;
	return 0;*/
	/*A* p1 = new A;*/
	A*p1 = (A*)malloc(sizeof(A));
}

我们进行编译:

c++内存管理:_第5张图片

 所以new相较于malloc对于自定义类型来说,new会调用自定义类型的构造函数,而malloc不会。

new会调用自定义类型的构造函数,那么delete是不是也会调用析构函数?

答:会:

int main()
{
	/*int *p1 = new int[10]{1, 2, 3, 4};
	delete[] p1;
	return 0;*/
	A* p1 = new A;
	/*A*p1 = (A*)malloc(sizeof(A));*/
	delete p1;
}

我们进行调用:

c++内存管理:_第6张图片

 所以delete也会调用自定义类型的析构函数。

我们举一个之前写的链表的例子:

struct ListNode
{
	ListNode(int val = 0)
	:_next(nullptr)
	, _val(val)
	{}
	ListNode* _next;
	int _val;
};

我们现在可以这样写链表:

struct和class都是类关键字,struct当我们不处理时,类中的成员的默认用public修饰。

我们可以在类中写构造函数,相当于我们之前的创建新节点

int main()
{
	ListNode*n1 = new ListNode(1);
	ListNode*n2 = new ListNode(2);
	ListNode*n3 = new ListNode(3);
	ListNode*n4 = new ListNode(4);
	n1->_next = n2;
}

这样写链表就会方便很多。

注意事项:

注意:

new和delete一定要匹配,否则会产生意想不到的问题,我们就只举一个例子:

例如:

nt main()
{
	A*p1 = new A[10];
	delete p1;
}

我们写出这样的代码进行运行就会报错:

c++内存管理:_第7张图片

为什么会这样呢?

我们先写一个正常的进行分析:

 

int main()
{
	A*p1 = new A[10];
	delete[] p1;
}

我们的A的成员只有一个整型,所以A占四个字节的空间,十个A就占40个字节的空间。

c++内存管理:_第8张图片

 我们先进行编译:

c++内存管理:_第9张图片

 我们进行申请时或进行释放时,都会调用多次调用构造函数或析构函数。

我们在创建时,知道我们需要创建十个A类所占的空间

但是我们在析构的时候,并不清楚我们需要析构多少次,这时候,我们需要额外申请一个整型的空间:

c++内存管理:_第10张图片

 接下来,我们把p1往前置:

c++内存管理:_第11张图片

 这时候,我们就知道我们需要析构多少次,并且从这里可以把我们申请的空间全部释放。

我们返回来看之前报错的情况:

 

int main()
{
	A*p1 = new A[10];
	delete p1;
}

为什么会报错呢?

答: 因为我们释放没有写[],所以我们构造了十次,但是我们不清楚析构了多少次,所以就会报错。

上面的这些都是关于编译器vs2013的一些情况,举这些例子只是为了说明一定要把申请的空间和delete释放的空间进行对应

new申请不需要检查返回值

我们知道malloc申请大的空间或者连续申请小的空间就会报错:

int main()
{
	while (1)
	{
		int *p1 = (int*)malloc(1024 * 1024);
		if (p1)
		{
			cout << p1 << endl;
		}
		else
		{
			cout << "申请失败" << endl;
			break;
		}
	}
}

并且我们知道,当malloc申请失败的时候,会返回空指针,所以我们可以写出以上代码来进行实验:

 c++内存管理:_第12张图片

 申请多次的时候,报错

接下来,我们对new进行实验:

int main()
{
	while (1)
	{
		int *p1 = new int[1024 * 1024];
		if (p1)
		{
			cout << p1 << endl;
		}
		else
		{
			cout << "申请失败" << endl;
			break;
		}
	}
}

c++内存管理:_第13张图片

 运行很快就停止了,但是并没有打印出申请失败,说明我们new失败的返回值并不是0,或者说,new失败没有返回值。

new失败的话,就会抛异常,所以我们不需要对返回值进行检查,对于抛异常的问题,我们之后再进行详解。

operator new和operator delete函数的讲解

上述的两个函数是new和delete的底层实现:

c++内存管理:_第14张图片

 new的底层实现就是通过new调用operator new函数,operator函数中有malloc,调用完毕之后调用构造函数。

注意:operator new函数并不是new的重载。

operator new相当于是一个新的全局函数。

我们看一下operator函数的定义:

c++内存管理:_第15张图片

 我们可以发现,operator new函数就是malloc函数的封装,无非就是加上了当申请失败时,不反回,而是抛异常。

我们观察一下operator delete函数。

c++内存管理:_第16张图片

 相当于我们operator的主体部分也是调用了free函数。

我们是否可以使用operator new来申请空间呢?

答:可以:

例如:

int main()
{
	while (1)
	{
		char*p1 = (char*)operator new(1024 * 1024 * 1024);
		cout << (void*)p1 << endl;
	}
}

operator new的使用方法和malloc相似。

不同点在于:operator new申请失败的话不需要报错,因为会抛异常。

你可能感兴趣的:(1024程序员节,c++)