目录
一.前言
二.new和delete的基本使用
1.new/delete操作内置类型
三.定位new表达式(placement-new)
四.new操作数出现内存申请错误时的处理方式:抛异常
五.new和malloc的区别
一.前言
C++沿用了C语言的底层内存管理机制:
然而在动态内存管理方面,C语言的动态内存管理机制(malloc/calloc/realloc/free)在两个主要方面上无法适用于C++中引入的复杂类对象:
- C语言的动态内存函数为类对象开辟堆区空间时,无法调用类的构造函数,free()函数释放类对象所占空间时无法调用类的析构函数。
- C语言的动态内存函数的报错机制不适用于C++的面向对象的编程模式。
二.new和delete的基本使用
1.new/delete操作内置类型
new和delete是C++中用于在程序中动态申请和释放堆区空间的操作符。
基本使用方法:
void Test() { 动态申请一个int类型的空间 int* ptr4 = new int; 动态申请一个int类型的空间并初始化为10 int* ptr5 = new int(10); 动态申请3个int类型的空间 int* ptr6 = new int[3]; 释放掉ptr4指向的堆区动态开辟的内存块 delete ptr4; ptr4 = nullptr; 释放掉ptr5指向的堆区动态开辟的内存块 delete ptr5; ptr5= nullptr; 释放掉ptr6指向的堆区动态开辟的内存块(对于连续开辟的空间要加[]) delete[] ptr6; ptr6 = nullptr; }
- 申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用
new[]和delete[],注意:匹配起来使用- new会根据申请创建的元素类型自动返回相应类型的指针,其返回值无须进行强制类型转换
- new申请的数组可以以如下的方式进行初始化:
2.new和delete操作自定义类型
new为一个类对象申请堆区内存空间后,会自动调用其构造函数:
class Date { public: Date(int year=0,int day =0) Date类的构造函数 :_day(day) ,_year(year) { cout << "constructor" << endl; } private: int _day; int _year; }; int main() { Date* ptr = new Date(2022,360); new一个Date对象 return 0; }
delete释放掉一个动态申请的类对象的内存空间前,会自动调用其析构函数。
class Date { public: Date(int year=0,int day =0) //Date的构造函数 :_day(day) ,_year(year) { cout << "constructor" << endl; } ~Date() //Date的析构函数 { cout << "destroy" << endl; } private: int _day; int _year; }; int main() { Date* ptr = new Date(2022,360); //new一个Date对象 delete ptr; //释放对象 return 0; }
C++设计new和delete这两个操作符的其中一个目的之一就是为了在动态申请和释放对象时能够调用其构造函数和析构函数,这一点对于一些申请了额外内存资源的复杂对象(比如栈对象)是非常重要的。
三.定位new表达式(placement-new)
定位new表达式用于调用已动态开辟好的类对象的构造函数初始化一个对象(即显式调构造函数)
使用格式:
(1)new (place_address) type(2)new (place_address) type(initializer-list)
place_address必须是一个指针(指向动态开辟的空间),initializer-list是类型的初始化列表
比如:class Date { public: Date(int year=0,int day =0) Date的构造函数 :_day(day) ,_year(year) { cout << "constructor" << endl; } ~Date() Date的析构函数 { cout << "destroy" << endl; } private: int _day; int _year; }; int main() { Date* ptr = (Date*)malloc(sizeof(Date)); malloc函数不会主动调用对象的构造函数 new (ptr)Date; 定位new表达式 ptr->~Date(); 显式调用析构函数 free(ptr); 释放对象 return 0; }
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,需要使用new的定义表达式进行显示调用类对象构造函数进行初始化
四.new操作数出现内存申请错误时的处理方式:抛异常
- 面向过程的语言(C语言),处理动态内存申请错误的方式是返回空指针并给出错误码
- 面向对象的语言,处理动态内存申请错误的方式一般是抛异常(通过对象来实现)--try catch
void test() { char* ptr = new char[0x7fffffff]; //申请2个G的堆区空间(过大会导致申请失败) } int main() { try try用于确定需要检测的代码语句 { test(); } catch (const std::exception&) catch用于捕获异常 { cout << "new failed" << endl; } return 0; }
- try用于确定需要检测的代码语句,catch用于捕获异常。
上述抛异常的报错机制更符合处理复杂对象的动态内存管理错误问题
五.new和malloc的区别
- malloc和free是函数,new和delete是操作符(关键字)。
- malloc申请的空间不会初始化,new可以初始化。
- malloc申请空间时,需要手动计算空间大小并传递,new只需在其后写上空间的类型即可,如果是多个对象,[ ]中指定对象个数即可。
- malloc的返回值为void*, 在使用时必须强转,new不需要。
- malloc申请空间失败时,返回的是NULL,因此使用时必须进行指针判空,new不需要,但是new需要捕获异常。
- 申请类对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理