内存管理(new+delete)

本文详细解析了C++中的内存分布,包括栈、堆、数据段和代码段的区别,以及new和delete的使用、与C语言malloc的区别,特别强调了new和delete在自定义类型中对构造和析构函数的调用。同时介绍了定位new的用法和内存池的应用。
摘要由CSDN通过智能技术生成

目录

1.内存分布

1.1栈

1.2堆

1.3数据段(静态区)

1.4代码段(常量区)

1.5图例里的空间位置分析

2.C++动态内存管理

2.1new和delete的基本使用

2.2new和delete与c语言的malloc等的本质区别

3.new和delete的实现

1.new

2.delete

3.new[]

4.delete[]

4.定位new


1.内存分布

1.1栈

一般的临时变量,都是存储在栈上,每一个函数都会占据一个栈帧空间,这个栈帧空间存储着这个函数的临时变量,如上面的a,mm,wwwwwww,p,j等。栈向下增长

1.2堆

注意动态开辟的空间是在堆上开辟的,除非你主动free掉,否则直到程序结束才会销毁。

但这里可能会跟上面的临时变量弄混,注意j这个指针,是开辟在栈里的,也就是说这个指针是只能在kk函数里面使用,但这个指针指向的是堆上的空间,这个空间除非free掉,否则在整个程序运行期间都可以访问。堆向上增长

1.3数据段(静态区)

还有就是全局变量和静态变量,静态变量本质上作用域没有变,如果放在全局,作用域就是当前文件里,放在函数里面,作用域就是这个函数,但在整个程序运行期间,静态变量只会定义一次,定义一次之后,再次遇到这个语句,会忽略掉。而全局变量作用域就是全局(一个工程的多个文件都能用)。两者都是开辟在数据段上的.

1.4代码段(常量区)

最后是常量和可执行代码,这两者都是只读数据,本身都不应该被修改。放在代码段里。

在栈和堆之间还有块内存映射段,这里不多解释,后期我再开个linux的专栏。

1.5图例里的空间位置分析

上面的wwwwwww是个数据,本身是在栈空间开辟,*wwwwwww也是在栈空间,因为1这个字符串常量,是在常量区也就是代码段里,而数组这边开辟的操作,本质是将这个字符串常量拷贝到了数组在栈空间开辟的空间里。

但下面的指针p不同,const修饰的是指向的char类型是个常量字符,根据字符串的规律,这里的*p指向的地址是1这个字符的地址,因此p这个指针变量是存在栈空间的,但*p(也就是这个字符串常量)是存在常量区也就是可执行代码上的。

sizeof(mm)=40;sizeof(wwwwwww)=2,sizeof(p)=4/8,strelen(wwwwwww)=2,strelen(p)=2,

sizeof(j)=4/8;

!!!!!!注意:malloc和calloc等c语言的动态函数,在我c语言的专栏里有,这里不多解释,主要是下面的new的delete。

2.C++动态内存管理

c语言的动态内存管理有点繁琐,因此c++推出了new和delete

2.1new和delete的基本使用

int main()
{
	int* a = new int;
	int* a1 = new int(3);
	int* a2 = new int[10];
	int* a3 = new int[3] {1, 2, 3};
	delete a;
	delete a1;
	delete[] a2;
	delete[] a3;
    return 0;
}
new加类型是基本的创建
加()是初始化单个空间为()里的值,比如上面初始化为3
[]是多个空间,这里形象上就是在堆上开辟了一段数组空间,有10个元素,每个元素都是int类型
多个空间初始化,可以用{};
注意多个空间初始化,假如我们{}里面写的数量跟开辟的数量不对,比如我们开辟了10个元素大小的空间
但只初始化了3个,那么前3个元素会是我们给的值,后面的都初始化为0


delete是用来释放空间的,如果是多个元素的,比如a2,那么delete后面要加[],要匹配的。

2.2new和delete与c语言的malloc等的本质区别

对于内置类型,两者基本没有区别,只是new和delete比较方便,但对于自定义类型

malloc等无法做到初始化。

class H
{
public:
	H(int a = 0)
		:_a(a)

	{
		
	}
	~H()
	{
		_a = 0;
	}
private:
	int _a;
};
int main()
{
	H* a1 = new H;
    H* a2=new H(2);
    delete a1;
    delete a2;
    H aa1(1);
    H aa2(1);
    H aa3(1);
    H*a3=new H[3]{aa1,aa2,aa3);
    //这里就是通过拷贝构造初始化
    H *a4=new H[3]{H(1),H(1),H(1)};
    //这里是通过匿名对象进行拷贝构造初始化
    H *a5=new H[3]{1,1,1};
    //这里是通过隐式转换,会先构造(常量直接作为值放进去)再拷贝构造
    //当然了上面的过程在编译器的优化下,都只有一次构造了。
	return 0;
}
对于new来说,在动态开辟空间的同时,还会自动调用一次构造函数,来初始化开辟的空间
对于delete来说,在释放动态空间的同时,还会自动调用一次析构函数。

对于自定义类型,new开的空间,是在堆上开辟了一个对象大小的空间,然后让指针指向堆上的这块空间,假如这个对象里的一个成员也是个指针,并在构造函数里new了一块空间给这个指针成员,那么这块空间是独立出来的,跟对象的空间不是在一起的,并且,是需要我们在析构函数里面delete的,因为对象的delete是针对的对象的空间,如果我们直接delete了对象空间,那么指针成员指向的空间就没有被我们主动释放,是错误的,也就是内存泄漏。

3.new和delete的实现

关于operator new和operator delete,这里不多解释,我们需要知道的是,这两个函数本质上就是封装了malloc和free。这两个函数,用法可以说跟malloc和free一模一样,就是把malloc和free的名字改下。区别就是这两个函数,比如operator new开空间失败后,是抛异常(先别管,只要知道这个机制,可以等我把异常的文章写了,或者去别的地方先看)。

之所以要有这两个函数,就是因为,new本身包含(开空间和调用构造函数),开空间如果直接用malloc,那么开空间失败后是返回空,这不符合c++的面向对象,为此封装后再使用,至于构造函数,那就直接调用即可,不用特殊处理。

当然了free没有那么多事,但为了配对,所以也有了封装,即operator delete。

总结下:

以下是针对自定义类型

1.new

先调用operator new开辟空间,对开辟的空间调用该对象的构造函数初始化。

2.delete

先对该对象的空间调用析构函数,完成后,再调用operator delete,释放对象的空间

3.new[]

先调用operator new[]开辟对象数组空间,实际上,operator new[]本质就是调用operator new开辟N个对象空间,注意不是调用N次operator new,而是直接开辟N倍的对象空间即可(实际上会多开辟4个字节,这4个字节是用来存储这个对象数组空间有多少个元素),因为是对象数组。然后在这N个对象空间里分别调用一次该对象的构造函数

4.delete[]

先在这N个对象空间里面分别调用该对象的析构函数,最后在调用operator delete[]释放整个对象数组空间,而operator delete[]本质上也是调用的operator delete,跟上面一样,直接释放整个空间(通过前面多开辟的4个字节,让delete知道要释放多大的空间)。

对于内置类型,我们只需知道,本质上就是直接malloc free。

稍微补充下,构造不可以显示调用,析构可以

4.定位new

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>

class H
{
public:
	H(int a = 0)
		:_a(a)

	{
		
	}
	~H()
	{
		_a = 0;
	}
private:
	int _a;
};



int main()
{
	H* a1 = (H*)operator new(sizeof(H));
	new(a1)H(2);
	//上面的语句就是定位new的写法,无参的话最后面括号可以不加
	//这样就可以显示调用构造函数
	a1->~H();
	free(a1);
	
	return 0;
}

 定位new在使用中一般是配合内存池使用(减少访问堆的次数,一次申请稍微大点的空间),内存池的空间没有被初始化,所以需要二次的显示调用构造,也是用定位new。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值