首页 > 系统相关 >C++内存管理基础

C++内存管理基础

时间:2023-08-01 21:01:59浏览次数:39  
标签:函数 int 基础 C++ 空间 内存 operator new delete

在c语言中内存管理函数为malloc和free,而在c++中内存管理的函数则是new和delete。

首先来看new和delete对于申请的内置类型的空间是如何处理的

内置类型的处理

申请连续的多个空间

void test1()
{
	int* ret = (int*)malloc(sizeof(int) * 10);
	int* rett = new int[10];//和malloc 一样都是在堆上申请10个整型的空间
	free(ret);
	delete[] rett;//这里是多个空间的开辟所以delete需要加[]
}

可以看到使用new明显简便很多,不需要强转。

申请单个的空间

void test2()
{
	int* ret = (int*)malloc(sizeof(int));
	int* rett = new int;//这里就只申请了一个整型大小的空间
	delete rett;//对于单个空间的释放不需要加[]
	free(ret);
}

依旧是比使用malloc简便很多

申请时即初始化

void test3()
{
	int* ret = (int*)calloc(1, sizeof(int));//创建单个整型的空间,并将内部的值初始化为0
	int* rett = new int(2);//创建单个整型的空间并将里面的值初始化为2
	free(ret);
	delete rett;//单个数据所以不需要[]
}

需要注意的是c++中new不能[]和()一起使用。还有便是申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],一定要匹配起来使用。

从上面就可以看出在内置类型的处理上,new只是简化了malloc的使用。

那么对于自定义类型呢?

这里以一个栈为例

自定义类型的处理

class stack
{
public:
	stack(int x = 4)
		:_capacity(4)
		,_size(0)
	{
		_a = new int[x];//开辟空间
	}

private:
	int* _a;
	int _capacity;
	int _size;
};//这只是一个简单的栈
int main()
{
	stack* st = (stack*)malloc(sizeof(stack));
	stack* sst = new stack;


	free(st);
	delete sst;
	return 0;
}

C++内存管理基础_析构函数

从监视上就可以看到,st并没有调用构造函数,而sst在创建空间之后还自动的调用了构造函数。而这也是为何要使用new而不是使用malloc,malloc只是将栈的空间创建好了但是对于内部的处理一个也不会做,而new则会在创建空间之后自动的调用构造函数。并且delete函数也会调用析构函数,而free则不会。

在讲解new和delete函数的实现原理之前需要先了解一个c++中封装的malloc 和free函数也就是operator new 和operator delete函数

operator new和operator delete 函数

C++内存管理基础_构造函数_02

C++内存管理基础_析构函数_03

那么这两个函数的功能是什么呢?

下面是功能演示

class stack
{
public:
	stack(int x = 4)
		:_capacity(4)
		,_size(0)
	{
		_a = new int[x];//开辟空间
	}
	~stack()
	{
		_capacity = 0;
		_size = 0;
		delete[] _a;
	}
private:
	int* _a;
	int _capacity;
	int _size;
};//这只是一个简单的栈
int main()
{
	stack* sst = (stack*)operator new(sizeof(stack));
	operator delete(sst);
	return 0;
}

C++内存管理基础_构造函数_04

从监视就可以看到这个函数和malloc函数可以说是一样的,不会调用构造函数,只会创建空间。

而new和delete的功能实现就离不开这两个函数

new和delete的实现原理

new的原理

  1. 调用operator new函数申请空间
  2. 在申请的空间上执行构造函数,完成对象的构造

delete的原理

  1. 在空间上执行析构函数,完成对象中资源的清理工作
  2. 调用operator delete函数释放对象的空间

new T[N]的原理

  1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对 象空间的申请
  2. 在申请的空间上执行N次构造函数 delete[]的原理
  3. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
  4. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释 放空间

需要注意的是operator delete和operator new并不是对new和delete的操作符重载,而是库中的函数名称就叫这个。那么如何验证呢?可以通过观察反汇编来验证。

C++内存管理基础_析构函数_05

C++内存管理基础_初始化_06

C++内存管理基础_析构函数_07

可以看到都调用了operatoe new和operator delete函数以及默认构造和析构函数。

前面说过new和delete的使用必须匹配那么如果不匹配会出现什么问题呢?

new和delete不匹配使用

首先如果是对于上面的栈类如果你使用new开辟空间,然后使用free释放空间,那么不用想百分百会出现问题,因为栈在构造函数是重新开辟了空间的,而free不会调用析构那么在构造函数那里开辟的空间就不会被释放造成内存泄漏。用图像表示:

C++内存管理基础_构造函数_08

那如果是下面这样呢?

class A
{
public:
	A(int x = 10)
		:_a(x)
	{
	}
private:
	int _a;
};//现在并没有显示写析构函数
void test5()
{
	A* a2 = new A[10];
	free(a2);
}
int main()
{
	test5();
	return 0;
}

如果你是这么写的那么就不会出现问题。但如果你是用下面的这种写法就会崩溃。

class A
{
public:
	A(int x = 10)
		:_a(x)
	{
	}
	~A()
	{
		cout << "~A()" << endl;
	}//显示写了析构函数
private:
	int _a;
};//现在并没有显示写析构函数
void test5()
{
	A* a2 = new A[10];
	free(a2);//这里即使你使用delete A也会报错
	//delete[] A //必须这么写才会正确不报错
}
int main()
{
	test5();
	return 0;
}

C++内存管理基础_初始化_09

那么为何会出现这种情况呢?

这就要涉及到new开辟空间的方式了如下图

C++内存管理基础_构造函数_10

首先当你使用new区开辟多个空间并且在这个自定义类型中含有析构函数的时候,在申请空间的时候会多调用四个字节的大小用来存10,而返还给p2的地址则如图。这个10便是用于delete函数要调用多少次析构函数的。如果你不正确的去使用delete[],那么最后调用operator delete函数的时候,会将存放10的那个空间略过,由此导致错误。如果你使用delete[]那么这个指针会先向前然后调用10次析构,最后从正确位置释放空间。

定位new表达式(placement-new)

定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。

使用格式: new (place_address) type或者new (place_address) type(initializer-list)

place_address必须是一个指针,initializer-list是类型的初始化列表

使用场景: 定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如 果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化

int main()
{
	A* ptr = (A*)operator new(sizeof(A));//如果这么写的话只是将类A的空间开辟出来了,并没有构造
	//如果是栈,那么如果不进行构造是不可能使用的,但是构造函数是不能显示调用的
	//析构函数可以显示调用
	//ptr->A();//这样是不允许的
	//ptr->~A();//析构函数可以显示调用
	//那若想显示调用构造就要使用定位new
	new(ptr)A(58);//括号里面填初始化参数
	ptr->~A();
	return 0;
}

C++内存管理基础_构造函数_11

当然定位new主要是用于内存池的,但是我暂时还没有学习到那里,所以只是先掌握定位new的使用。

异常处理

在c中,如果malloc开辟空间失败必须使用if去判断然后手动去返回-1,而c++除了这一种方法外还增加了一种方法也就是抛异常。使用如下

int main()
{

	try {
		char* ptr = new char[0x7fffffff];//创建一个不可能创建出的空间在32位下
	}
	catch (const exception& e)//捕捉异常
	{
		cout << e.what() << endl;
	}
}

C++内存管理基础_析构函数_12

需要注意的是即使你将创建空间的函数封装到func函数里面,然后再func函数里面创建空间之后还做了一些操作,只要创建空间失败,会直接从函数中跳出和goto一样直接去执行catch。如果开辟空间成功则不会进行catch操作。当然你创建空间的函数必须放到try中否则也不会处理异常

当然这里我也只是暂时掌握基本用法而已。


希望这篇博客能对你有所帮助,如果发现了错误,欢迎指出。


标签:函数,int,基础,C++,空间,内存,operator,new,delete
From: https://blog.51cto.com/u_15838996/6927811

相关文章

  • C++入门到放弃(05)——内联函数: inline
    ​1.基本用法要使用内联函数时,必须遵循如下规则:在函数声明前加上关键字inline,或着在函数定义前加上关键字inline,二者取其一即可。inlineintmax(inta,intb);//取其一即可inlineintmax(inta,intb){//取其一即可returna>b?a:b;}另外一种做......
  • 基础算法串讲
    线性数据结构链表std::list是STL中的链表特点:是一条链,空间复杂度\(O(n)\)插入与删除十分方便,时间复杂度\(O(1)\)寻找与查询数据比较麻烦,时间复杂度\(O(n)\)数组大小固定,链表大小可动态调整注意:std::vector不算数组,是数据结构链表的分类单向链表:每一个结点......
  • Java基础数据类型
    基础数据类型基础数据类型:byte(字节型),short(短整型),int(整型),long(长整型),float(单精度浮点型),double(双精度浮点型),char(字符型)  1.byte字节型占1个字节,范围-128到127bytea=5;byteb=6;//bytec=200;//编译错误,超出范围2.short短整型占2个字节,范围-32768......
  • 初学C语言day07--指针与堆内存
    什么是指针:指针是一种特殊的数据类型,使用它可以定义指针变量,指针变量中存储的是整形数据,该整型数据代表了内存的编号(地址),可以通过这个编号访问对应的内存为什么要使用指针:1、函数之间是相互独立的,但是有时候需要共享变量传参是单向值传递全局变量可以共享,但是容易命名冲突......
  • 20230801 数论基础学习笔记
    理论基础中国剩余定理及拓展已知\(x\equiva_i(\bmodp_i\)\),求\(x\bmod\operatorname{lcm}\{p_i\}\)的值。若\(p_i\)互质,那么我们只需要计算\(c_i\)使得\[\prod\limits_{j\nei}\cdotc_i\bmodp_i=1\]然后有\[x=\sum\limits_{i}c_ia_i\prod\limits......
  • Python基础day56 Django视图层相关
    视图层三板斧问题在视图函数中写函数跟普通函数不太一样,Django中使用的是局部的request所有的视图函数不能够没有返回值,并且返回值还必须是HttpResponse对象#错误代码Theviewapp01.views.indexdidn'treturnanHttpResponseobject.ItreturnedNoneinstead.其实我......
  • javaScript基础(3)
    string字符串1.字符串必须用‘’或者“”,引起来的一段字符内容,在表示字符串的时候,不能在双引号表示的字符串中使用双引号2.字符串可以是空的字符串3.字符串双引号或者单引号里可以嵌套另一种字符串的引号4.空格在字符串里是占位的varsty1=“123123”5.获取字符串......
  • Redis基础
    1.Redis入门1.1Redis简介Redis是一个基于内存的key-value结构数据库。Redis是互联网技术领域使用最为广泛的存储中间件。官网:https://redis.io中文网:https://www.redis.net.cn/key-value结构存储:主要特点:基于内存存储,读写性能高适合存储热点数据(热点商品、资讯、新闻......
  • 拼多多店铺订单API接口(pdd.order.basic.list.get订单基础信息列表查询接口)代码对接教
    拼多多店铺订单API接口(pdd.order.basic.list.get订单基础信息列表查询接口)代码对接教程如下:1.公共请求参数参数名称参数类型是否必填参数描述(接口代码教程wx19970108018)typeString必填API接口名称(点击获取请求key和secret)client_idString必填POP分配给应用的client_idaccess_tok......
  • 计算机基础与JavaScript初识
    一:编程介绍编程:就是让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并最终得到结果的过程.计算机程序:就是计算机所执行的一系列的指令集合,而程序全部都是用我们所掌握的语言来编写的,所以人们要控制计算机一定要通过计算机语言向计算机发出命令。从事编程的人员,就是程......