目录
2. operator new与operator delete函数
1.动态内存管理
1.1new和delete的使用
对于new和delete操作符的使用,new和delete与C语言中的malloc和free的使用是相似的,new的功能对应malloc,是用于动态开辟一段空间,delete的功能对应free,是用于释放所开辟的空间。但要注意区分new和delete是操作符,malloc和free是函数。
使用:对于不同的开辟元素个数,有着两种情况,使用时两组需要进行固定搭配使用,不要混搭使用,更不要配合C语言中的malloc和free函数使用,否则会出现意料之外的运行错误,导致程序崩掉。
1.new + 类型 2.new +类型[对象个数]
delete + 待释放空间 delete[] + 待释放空间
new和delete的使用对应两组不同的情况,一种是使用new动态开辟单个元素空间,此时需要搭配delete来释放空间,另一种是使用new配合 [ ] 进行的多个元素的动态内存开辟,此时需要配合delete[]来释放空间。此外,需要注意的是delete的使用对于动态开辟内存空间的释放与C语言是一致的,不能进行局部空间的释放,只能从头开始一次性全部释放。
1.2对内置类型的操作
对与内置类型的操作,new和delete的作用分别与malloc和free的作用是相同的,new同样是只起到动态开辟内存的作用,delete也是只起到释放空间的作用。
new的使用在开辟单个或多个元素的情况下,也可以对开辟空间中的元素进行初始化,对于单个元素的初始化我们使用(),而对于多个元素的初始化使用{},
下面给出一些简单的例子:
单个元素内存空间的开辟:
对于单个元素的内存开辟,我们可以使用()进行元素初始化,未初始化的内存中存储的是随机值。
多个元素内存空间的开辟:
对于多个元素的内存开辟,我们使用{}进行初始化,与数组的元素的初始化如出一辙,对于不完全初始化,的剩余空间会默认置0,同于不进行初始化的,所存放的也是随机值。
1.3对自定义类型的操作
对于自定义类型的操作,new和delete的使用与C语言中的malloc和free的就有较大的差异了,C语言中的malloc和free在处理自定义类型一般是不太适用的
差异:
new在动态开辟自定义类类型的内存空间后,会自动调用该类类型的默认构造。
delete在释放空间之前,会先自动调用该类类型的析构函数。
class A
{
private:
int _a;
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a = 0)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
};
我们通过new先后动态开辟的A类型的两段内存空间,最后运行结果会发现,p1所指向的空间在被开辟后,调用了类的构造函数,而在delete调p1所指向的空间后,显示器上也紧跟着一个析构函数的调用显示,同样对于指向,有3个元素空间的p2,在使用new和delete后都分别调用三次构造和析构函数。
2. operator new与operator delete函数
new和delete是用户进行动态内存申请和释放的操作符,而operator new 和operator delete是系统提供的全局函数new在底层调用operator new全局函数来申请空间,delete通过operator delete全局函数来释放空间。
1.operator new:
operator new 函数实际上是一个对于malloc函数进行封装的函数,其进行的动态内存的申请最终也是通过malloc函数来实现的。malloc函数在申请空间成功后,会直接返回,而如果失败则会返回空,operator new 函数则对malloc函数申请空间失败情况进行了处理,在申请空间失败后向用户抛出异常。
2.operator delete:
对于operator delete函数,我们可以认为它与C语言中的free函数是相同的作用,没有什么区分,operator delete的出现主要是为了配对operator new的使用。
3.operator new[] 和operator delete[]
对于operator new[] 和operator delete[]函数,在实际上使用时,operator new[] 是对operator new函数的多次调用,完成对多个元素内存空间的开辟,同样,operator delete[]函数也是对operator delete的多次调用。
operator new 和 operator delete的使用,在用法上与 malloc和 free上也是相同的。
int main()
{
int* p1 = (int *)operator new(sizeof(int));
int* p2 = (int*)malloc(sizeof(int));
operator delete (p1);
free(p2);
return 0;
}
3. new和delete的实现原理
对于不同类型的动态内存空间申请释放,new和delete会有不同的处理。
3.1 内置类型
对于内置类型的动态内存开辟释放:
new:
new / new[]操作符的本质是去调用函数operator new / new[],此外不做其他处理。而operator new 函数的本质又是一个对于malloc函数的封装,对malloc函数申请空间失败时会抛出异常的版本。
过程:new -> operator new -> malloc
delete:
delete / delete[]操作符的本质是去调用函数operator delete / delete[],此外不做其他理。而operator delete的本质是去调用free函数。
过程:delete -> operator delete -> free
所以我们可以认为在对内置类型操作时,new和delete与C语言中的malloc和free是具有不同用法但相同功能的区别。
3.2自定义类型
对于自定义类型的空间申请释放,new和delete会做更多的处理。
new:
处理自定义类型时,new会额外调用该类的构造函数进行处理,在申请的空间上执行构造函数,完成对象的构造
过程:
1.new -> operator new -> malloc
2.调用构造函数,完成申请空间上对象的初始化。
delete:
处理自定义类型时,delete会先调用对象中的析构函数去销毁类对象成员的空间,然后再调用operator delete/delete[]。
过程:
1.调用析构函数销毁类对象成员内存空间。
2.delete -> operator delete -> free
4.定位new表达式的使用
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
对于已经预先开辟好对应类空间的情况,我们不能够直接去调用类中的构造函数,而要通过new表达式才能够调用构造函数初始化对象。定位new表达式在实际中一般是配合内存池使用,因为内存池分配出的内存没有初始化,所以如 果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
使用格式:
带参的构造:new (place_address) 类名
不带参的构造:new (place_address)类名(initializer-list)
class A
{
private:
int _a;
public:
A(int a = 0)
: _a(a)
{
cout << this << ":" << "A()" << endl;
}
~A()
{
cout << this <<":" << "~A()" << endl;
}
};
我们使用operator new函数预先开辟好储存A对象的空间,如果我们要对类对象进行初始化,需要使用new表达式,选择调用带参与否的构造函数,不能使用指针加->的形式去调用构造函数,但我们可以通过指针加->的形式去调用类中的析构函数。
5.new、malloc和delete、free的区别
相同点:
都是从堆区上申请空间,并且需手动释放。
不同点:
1.new和delete是操作符,malloc和free是函数。
2.new申请的内存空间可以初始化(内置、自定义类都可以),malloc不可以。
3.malloc申请空间时,需要手动计算要申请的空间大小并传递,而new只需在其后跟上空间的类型即可, 如果是多个对象,在[]中指定对象个数即可。4.malloc申请空间失败时,返回的是空(NULL),因此使用时必须判空,new不需要,new会抛出异常,但是需要捕获异常。5.对于自定义类型空间的申请释放,new会在申请空间后,调用构造函数初始化对象,malloc不会;delete在释放空间前会调用析构函数去清理类对象中的空间,free只会释放当前动态开辟的空间。6.malloc的返回值为void*, 在使用时必须强转成对应类型,new不需要,new后跟的是空间的类型。
结语
这节对于C++中动态内存的知识介绍到这里就结束了,下期将继续带来C++相关的后续知识,感谢各位的观看,有错误或者需要改进的地方还请指出,如果有帮助的话,还请点个赞呀!