C/C++动态内存管理 ,new和delete

目录

C++动态内存管理

内置类型的动态内存管理

自定义类型的动态内存管理

new和delete的实现原理

new和malloc、delete和free的异同

new的其他用法:定位new


C语言动态内存管理<-点这里

C++动态内存管理

因为C语言中的管理方式使用起来不方便而且有些情况下无法解决问题,C++通过newdelete关键字进行动态内存管理,delete和new必须配套使用,不能new开辟空间使用free释放空间。


内置类型的动态内存管理

//动态申请1个int类型的空间
int* p1 = new int;
delete p1;

//动态申请1个int类型的空间并初始化为10
int* p2 = new int(10);
delete p2;

//动态申请10个int类型的空间
int* p3 = new int[10];
delete[] p3;

//动态申请10个int类型的空间并初始化
int* p4 = new int[10]{
    
    1,2,3,4,5,6,7,8,9,10};
delete[] p4;

//动态申请一个10行10列的二维数组
//int(*p)[n] 是一个数组指针,该数组有10个int类元素
int(*arr1)[10] = new int[10][10];
delete[] arr1;

//用二级指针模拟实现10行10列的二维数组
//内存开辟
int** arr2 = new int*[10];
for (int i = 0;i < 10;i++)
{
   arr2[i] = new int[10];
}
//空间释放
for (int i = 0;i < 10;i++)
{
    delete[] arr2[i];
}
delete[] arr2;

申请和释放单个元素的空间使用new和delete,申请和释放连续的空间使用new[]和delete[]


自定义类型的动态内存管理

new为自定义类型分配空间后,会调用构造函数,delete释放自定义类型空间前会调用析构函数

class Stack
{
private:
    int* _arr;
    int _capacity;
    int _top;
public:
    Stack()
        :_capacity(4)
        ,_top(0)
    {
        _arr = new int[4];
    }
    Stack(int capacity)
        :_capacity(capacity)
        ,_top(0)
    {
        _arr = new int[capacity];
    }
    ~Stack()
    {
        delete[] _arr;
    }
};

int main()
{
    Stack* st1 = new Stack(10);
    delete st1;
    Stack* st2 = new Stack[3];
    delete[] st2;
    Stack* st3 = new Stack[3]{ 5,2,4 };
    delete[] st3;
    Stack* st4 = new Stack[3]{ Stack(5),Stack(2),Stack(4) };
    delete[] st4;
}

注意:自定义类型数组初始化有多种方法,如果有单参构造函数或者除了缺省参数只有一个参数的构造函数,数组初始化可以直接用该参数类型的值,直接放到{ }内初始化,因为它会自动转换成对象来初始化数组;还可以用匿名对象初始化。


new和delete的实现原理

因为malloc和calloc的功能是开辟空间,如果开辟空间失败返回0。但是对于C++面向对象的语言来说,往往遇到问题要抛异常,所以会用operator new来封装malloc,以达到抛异常的功能。operator 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[]的原理
1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释
放空间


new和malloc、delete和free的异同

相同点:new和malloc、delete和free都是从堆上申请空间,都需要对空间手动释放

不同点:

  • (特性)new/delete是关键字,malloc/free是函数
  • (特性)malloc使用时需要计算空间大小,new会根据数据类型自动计算,如果有多个数据直接在[ ]指定数量即可
  • (特性)malloc的返回值是void*,使用时需要强制类型转换,而new不需要。
  • (特性)malloc申请空间失败返回NULL,而new需要捕获异常
  • (功能)malloc申请的空间不会初始化,new申请的空间可以初始化
  • (最重要)(功能)malloc/free只会开辟和释放空间,而new会先调用operator new开辟空间再调用构造函数初始化,delete会先调用析构函数完成空间资源的清理再调用operator delete释放空间。

new的其他用法:定位new

作用:在已经开辟好的一块空间调用构造函数初始化

格式:new(address)custom type(initial list)或者new(address)basic type(initial list)

address是开辟的空间的地址;

custom type表示自定义类型;

initial list表示自定义类型的初始化列表;

basic type表示基本数据类型

使用场景:

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

优势:

内存的申请和释放有一定的开销,遇到需要频繁申请释放内存的情况时,可以一次性开好空间,使用定位new调用构造函数初始化,可以提高效率。

class Date
{
    friend ostream& operator<<(ostream& _cout, const Date& d);
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2023, int month = 1, int day = 1)
        :_year(year),
        _month(month),
        _day(day)
    {}
    ~Date()
    {}
};

//流插入运算符重载
ostream&  operator<<(ostream& _cout,const Date& d)
{
    _cout << d._year << "-" << d._month << "-" << d._day;
    return _cout;
}

int main()
{
    //基本数据类型
    int i = 5;
    int* p1 = (int*)malloc(sizeof(int));

    new(p1)int(10);
    cout << *p1 << endl;//10

    new(p1)int(i);
    cout << *p1 << endl;//5

    free(p1);

    //自定义数据类型
    Date* p2 = (Date*)malloc(sizeof(Date));

    new(p2)Date();//无参时,初始化的()可加可不加
    cout << *p2 << endl;//2023-1-1

    new(p2)Date(2023, 5, 20);
    cout << *p2 << endl;//2023-5-20

    p2->~Date();
    free(p2);
}

猜你喜欢

转载自blog.csdn.net/weixin_74269833/article/details/131004813