C++中的内存管理

简介: C++中的内存管理

1 C++内存分布

C++根据不同的数据需求有着不同的存储特性,所以将内存分为多个区域来存储数据!


2 C++内存管理方式

在C语言中,我们是利用malloc/calloc/relloc进行动态内存管理!其中relloc就是对之前malloc申请的空间进行扩展,calloc就是malloc加上memset(),而C++是通过new和delete操作符进行动态内存管理


2.1 处理内置类型

利用new和delete操作符进行动态内存管理比C语言中的malloc方便好写了许多!

int main()
{
  int* b1 = new int;    //申请一个int的空间
  int* b2 = new int[5]; //利用new[]操作符,申请5个连续int的空间,就是一个数组
  delete b1;
  delete[] b2;
  return 0;
}


通过调试,我们可以进一步的了解new和delete操作符是如何处理内置类型的!

c25cae56f41ebe3f480538305dc84b59_bbee10e82fa94ba8999442213b6453d8.png

可以发现,new其实和C语言中的malloc是一样的,也是不会进行初始化的,只是动态开辟空间!相比较于malloc而言,new是不用进行强制类型转换,也不用自己去计算要开辟空间的大小了!同理,delete就相当于free,释放对应开辟的空间!


注意:new和delete要搭配使用,new[]和delete[]搭配使用!不要混着使用,否则会出现内存泄露或者程序不能正常运行的问题!

利用new其实也是可以对内置类型进行初始化的!代码如下:

int main()
{
  int* p1 = new int(10); //开辟一个int空间,结合()中的内容进行初始化
  int* p2 = new int[5] {1, 2, 3}; //开辟5个连续的int空间,结合{}中的内容进行初始化

  delete p1;
  delete[] p2;
  return 0;
}

调试结果如图所示:

可以发现对于5个连续的int空间,只要我们给定一些空间的初始化,后面的空间编译器就把它初始化成为0的!

2.2 处理自定义类型

对于自定义类型如果使用malloc,我们就无法对自定义类型进行初始化!因为构造函数不可以通过所给的对象指针直接调用:

利用new和delete操作符如何处理自定义类型呢?我们可以参考如下代码进行理解:

class Date {
private:
  int _year;
  int _month;
  int _day;

public:
  Date(int year = 2023 , int month = 11, int day = 21)
  {
    cout << "Date 构造函数调用了" << endl;
    _year = year;
    _month = month;
    _day = day;
  }

  ~Date(){
    cout << "析构函数被调用了" << endl;
  }
};

int main()
{
  Date* d1 = new Date(2023,11,21);
  Date* d2 = new Date[5]{ Date(2023,11,20) };//初始化一部分,其他的是默认构造初始化,如果没有默认构造也会报错
  Date* d3 = new Date;//调用默认构造,无默认构造就会报错

  delete d1;
  delete[] d2;
  delete d3;
  return 0;
}

运行结果如下:

从以上我们就可以知道new和delete处理自定义类型的原理

new就是开辟对象空间+调用构造函数,malloc就不会调用构造函数

delete就是调用析构函数+释放对象空间,free就不会调用析构函数


3 operator new与operator delete函数

operator new和operator delete是系统提供的全局函数!我们先来看这样一段代码:

class Stack {
private:
  int* p;
  int _capacity;
  int _top;

public:
  Stack(int capacity = 4)
  {
    cout << "Stack(int capacity = 4)" << endl;
    p = new int[capacity];
    _capacity = capacity;
    _top = 0;
  }

  ~Stack()
  {
    cout << "~Stack()" << endl;
    delete[] p;
    _capacity = _top = 0;
    p = nullptr;
  }

};
int main()
{
  Stack st1;
  Stack* st2 = new Stack;
  return 0;
}

如何理解上述的两个new呢?如图所示:


第一个new是开辟对象的空间,第二个new是开辟对象中p所指的空间!这也就和之前的知识点联系上了,利用delete就是先调用析构,清理对象中的额外资源也就是动态开辟所需的空间!在释放对象的空间!通过这个例子,我们可以更深刻的理解new和delete的工作原理!那么new和delete底层是怎样实现的呢?

运行以下这段代码:

int main()
{
  Stack* st1 = new Stack;
  Stack* st2 = (Stack*)operator new(sizeof(Stack));
  return 0;
}

调试与运行结果如图:

我们可以发现通过operator new函数和malloc一样,只是开辟了空间,并不会调用构造函数进行初始化!实际上operator new底层封装的就是malloc!那为什么不直接用malloc呢?那是因为我们要面向对象编程,malloc返回值是0,我们通过operator new是可以抛异常的!更加符合面向对象编程!而operator delete也同理,是封装了free!简单来说既可以用下图来概括!


4 定位new表达式

可以通过定位new显式的调用构造函数!在实际应用中,定位new一般是配合内存池使用的,因为内存池分配出来的空间没有初始化,因此如果需要在这块内存池分配出来的空间上构造自定义类型的对象,需要使用定位new显式调用构造函数构造目标对象


格式如下:

new(申请对象地址)类型

new(申请对象地址)类型(类型的初始化列表)

例子如下:

//定位new的使用

class A {
private:
  int _a;
  int _b;

public:

  A(int a = 10)
  {
    cout <<"A(int a = 10)"<< endl;
    _a = a;
  }

  A(int a , int b )
  {
    cout << "A(int a = 1, int b = 2)" << endl;
  }

  ~A()
  {
    cout << "~A()" << endl;
  }
};

int main()
{
  A* a1 = (A*)operator new(sizeof(A));//开辟对象的空间
  new(a1)A;//定位new,显式的调用构造函数,调用默认构造
  a1->~A();//显式的调用析构函数
  free(a1);//释放对象空间

  A* a2 = (A*)operator new(sizeof(A));
  new(a2)A(10, 20);//定位new,调用两个参数的构造函数
  a2->~A();
  free(a2);
}

显式调用了构造函数,那就要显式调用析构函数,要配对使用就可以了!

5 malloc/free和new/delete的区别

相同点:都是从堆上开辟空间,都需要手动的进行释放!

不同之处在于:

1️⃣malloc和free是函数,而new和delete是操作符。

2️⃣malloc不会进行初始化,new开辟空间的同时可以初始化。

3️⃣malloc开辟失败是返回NULL需要我们进行判断,new开辟失败是抛出异常。

4️⃣malloc开辟空间需要手动计算大小并进行传递,而new就可以不用去计算大小!

5️⃣对于自定义类型,malloc不会去调用构造函数,free不会去调用析构函数!而new会去调用构造函数进行初始化,delete会去调用析构函数进行清理!

目录
相关文章
|
5天前
|
存储 Java C++
C++ 引用和指针:内存地址、创建方法及应用解析
C++中的引用是现有变量的别名,创建时需用`&`运算符,如`string &meal = food;`。指针存储变量的内存地址,使用`*`创建,如`string* ptr = &food;`。引用必须初始化且不可为空,而指针可初始化为空。引用在函数参数传递和提高效率时有用,指针适用于动态内存分配和复杂数据结构操作。选择使用取决于具体需求。
41 9
|
3天前
|
存储 编译器 Linux
|
5天前
|
存储 Linux C语言
【C++从练气到飞升】07---内存管理
【C++从练气到飞升】07---内存管理
|
5天前
|
存储 编译器 C++
【C++】内存管理和模板基础(new、delete、类及函数模板)
【C++】内存管理和模板基础(new、delete、类及函数模板)
25 1
|
5天前
|
存储 缓存 算法
C++从入门到精通:4.6性能优化——深入理解算法与内存优化
C++从入门到精通:4.6性能优化——深入理解算法与内存优化
|
5天前
|
存储 程序员 编译器
C++从入门到精通:3.4深入理解内存管理机制
C++从入门到精通:3.4深入理解内存管理机制
|
5天前
|
存储 人工智能 程序员
【重学C++】【内存】关于C++内存分区,你可能忽视的那些细节
【重学C++】【内存】关于C++内存分区,你可能忽视的那些细节
57 1
|
5天前
|
C语言 C++
【C++基础(九)】C++内存管理--new一个对象出来
【C++基础(九)】C++内存管理--new一个对象出来
|
5天前
|
存储 编译器 Linux
c++的学习之路:8、内存管理与模板
c++的学习之路:8、内存管理与模板
11 0