目录
[3.operator new/operator delete函数](#3.operator new/operator delete函数)
1.new用法说明
(1)在C语言阶段,我们无论是为数组开辟空间,还是为单个的内置数据类型的数据开辟空间,都是使用malloc,realloc,calloc这些函数,但是C++里面引入了类和对象,这个时候,我们是没有办法使用C语言里面的那些函数为类开辟空间的;
(2)我们使先看一下new为单个的内置数据类型开辟空间的使用方法:
这个就是C语言和C++的不同的写法,C语言需要进行强制类型转换,但是C++不需要;
在C++里面,我们可以直接使用new为int类型的数据开辟空间写法就如同上面的,而且C++里面使用new开辟空间的时候,不需要进行强制类型的转换,我们在开辟完成之后可以直接进行初始化的操作,初始化的方法就是上面展示的在int后面加上一个括号,括号里面的数值就是我们想要初始化的数值;
(3)new为数组开辟空间
数组开辟空间的方式稍有不同,因为数组里面有很多个数据,我们需要在int后面加上中括号,里面的数据就是数组里面的元素的个数;
初始化的方法就如同上面展示的那样,在[10]后面加上大括号,里面写入的就是我们想要初始化的结果,我们这个案例是对数组里面的前三个元素进行初始化,后面的其他的元素默认初始化的结果是0;
(4)手动开辟空间的释放
我们在学习C语言的时候,是使用free进行空间的释放,在C++里面,我们引入了delete进行释放空间,delete的使用方法,如下所示:
我们还是分为单个数据的释放和数组的整体空间的释放进行介绍 :
显然,数组和单个数据类型的不同就在于数组是在delete后面加上了方括号来进行标识;
(5)类创建的对象的释放
如果仅仅是为了方便代码的简洁性,祖师爷肯定不会发明new这个东西出来,任何一个事物的出现必然是可以解决一类问题的,这个new是可以解决malloc这些函数无法完成的问题的,因为malloc是没有办法对我们的类创建的对象进行空间的开辟的,因为malloc根本就不认识这个类和对象,这个时候就有new和delete的用武之地了;
这里还是使用相同的方法,只不过把原来的内置数据类型转换为我们现在的自定义的数据类型,其他都是不变的;
但是这里必须要在类里面写出构造函数(初始化列表进行初始化,有缺省参数),否则我们在对A里面的p02进行初始化的时候就会报错(这里建议我们以后进行初始化的时候尽量使用初始化列表的方式);
我们也可以使用隐式类型转换和匿名对象的方式对这个数组元素进行初始化:
(6)new/delete和malloc/free最大的区别就是,new/delete对于自定义类型的数据,除了会进行空间的开辟,还会调用对应的构造函数;
(7)使用new创建链表里面的节点
之前我们是使用buynode这个函数进行节点的创建的,现在我们可以使用new进行创建节点,而且我们可以直接进行初始化的操作。
2.new/delete在栈里面的运用
(1)我们使用栈举例,可以更好地理解new /delete的用法,我们都知道对于一个栈而言,是有一个数组,一个变量记录数组的容量,一个变量用来记录数组里面的元素的个数;
(2)当我们使用new的时候,这个时候开辟的是栈的空间,就是上面的图片里面的正方形的那片空间,当我们在使用new开辟空间的时候,因为这个时候就会调用默认的构造函数,这个时候调用默认构造函数的时候就会为栈里面的数组元素开辟空间;
(3)当我们使用delete释放空间的时候,首先调用析构函数释放掉我们为数组元素开辟的空间,然后是使用delete释放掉栈的空间。
3.operator new/operator delete函数
(1)首先在这里声明一下,上面的这两个是函数,虽然和我们之前学过的运算符重载长得很像,但是这个是标准库里面的两个函数,不是运算符重载;
(2)了解这两个函数,可以让我们更加清晰的认识new的底层逻辑:就是我们每次使用new进行空间的开辟的时候,实际上是调用的operator函数和构造函数,而这里的operotor函数实际上就是对于malloc的一个封装,不同的地方就在于我们的malloc开辟空间失败就是返回0,但是这个封装malloc的operator new函数实际上,当空间没有开辟成功的时候,不会返回0,而是抛出异常;
(3)抛出异常就是对于错误的代码就不会执行了,基本的格式就是try catch语句,try里面的就是执行语句,当我们在try里面的语句出现问题的时候,就会跳过try里面错误语句后面的代码,通过catch打印错误原因;
(4)这个operator new/operator delete函数不是给我们使用的,是给标准库使用的,我们一般不会使用到;
(5)可见,C++里面的new并不是特别神秘,只是在封装函数的基础上面加上构造函数,有了new,我们就可以不用判断if(......==NULL)perror这样的判断,而是在失败的时候利用try---catch语句抛出异常,这样其实更加符合C++的机制和报错原理;
(7)在C++里面是兼容C语言的,所以我们在使用new进行空间的开辟的时候,也是可以使用free进行空间的释放的,但是这个并不代表我们可以随意的使用;
实际上,我们在写析构函数和不写析构函数的时候,我们的编译器的执行原理是不一样的,这个地方那个我们只需要记住,不要把new和free混用,一定要匹配使用才行,否则就会出现意想不到的报错,就是使用new int 开辟空间,就是用delete释放空间,使用new int [10]开辟数组空间,就使用delete[ ] int进行空间的释放,不要把C++的delete和C语言的free混合使用,仅此而已。
4.构造函数的显式调用
(1)我们这里是使用一个栈作为例子,我们可以在82行显式调用Stack的析构函数,但是我们没有办法像79行一样显式调用构造函数;
(2)我们想要显式调用构造函数,就要像80行一样进行表示,这样才能够显式调用构造函数,但是这样的表示方法我们一般不会使用;
(3)这个语法有很多种叫法,例如定位new表达式,或者有的叫做replacement new都是可以的,我们下面还可以使用一个类介绍一下:
上面的这个就比栈的那个更加完整,更加易于我们进行理解,实际上我们通过这两个对比更能够说明问题:
Stack和这里的A本质都是一样的,我们开辟空间的时候malloc和operator new本质都是一样的,也就可以理解为两者实现的效果是一样的;
我们在进行显示调用的时候都是new(定义的变量)类的名字;
实际上这个定位new这个语法适合内存池结合使用的,我们现在了解即可,因为我们一般不会这样使用,内存池里面的呢内存不会进行初始化的操作,因此我们需要像现在这样显示的调用构造函数进行初始化的操作,仅此而已。
5.malloc&&new&&free&&delete区别
(1)对于这两者的区别以及他们之间的比较,我们主要是通过用法以及他们的底层原理进行介绍的;
(2)在使用方法上面,malloc和free都是函数,但是delete和new都是操作符;
(3)malloc不会进行初始化的操作,但是我们的new操作符可以进行初始化,初始化的方法就是对于单个的变量,我们可以使用小括号进行初始化,对于这个数组里面的多个元素的情况,我们可以使用花括号的方式进行初始化的操作;
(4)malloc申请空间的时候,需要我们手动的进行计算空间的大小,但是如果我们使用new进行空间的开辟的时候,就可以直接在new后面加上数据的类型就可以了,是不需要我们进行手动的释放的,而且对于数组类型的,我们可以直接在new后面类型后面加上中括号里面写上数组里面的元素的个数即可;
(5)malloc需要进行强制类型的转换,但是new不需要,因为new的后面就是数据的类型;
(6)malloc申请空间失败的时候,返回值是NULL,而且我们需要进行判断是否开辟空间成功,但是对于new而言,就不会出现这个情况,因为new开辟空间失败的话就会抛出异常;
(7)对于底层原理而言,我们的malloc就会进行空间的开辟,不会调用构造函数和析构函数,但是new开辟空间的时候,就会调用构造函数完成对于对象的初始化,delete进行空间的释放的时候,就会调用析构函数进行对象的销毁,完成资源的清理;
(8)总的来说,我们需要从两个方面认识到为什么C++里面要引入new和delete,
一方面就是为了让我们对于空间的开辟的时后,更加简洁,这个简洁体现在
不用进行初始化,
不用担心开辟失败的问题,
不用进行强制类型的转换,
不用在多个数据的时候计算空间的大小;
另一方面就是解决对于自定义类型的空间开辟的问题,让这个delete、new自动调用析构函数和构造函数进行空间的初始化以及内容的清理工作。