C++中的动态内存管理

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

C++中的动态内存管理

1. 内存分布与虚拟地址空间

每一个加载到内存中的进程,都有一个虚拟地址空间,再经过页表映射到物理内存空间。

2. C语言的动态内存管理

malloc:动态开辟空间,不会初始化。

calloc:动态开辟空间+初始化。

realloc:堆动态开辟的空间进行重新分配。

free:释放动态开辟的空间。

上面这些都是函数调用。

3. C++的动态内存管理

new:动态申请内存

delete:释放内存

批量动态内存管理:new []delete []

int main()
{
  int* a = new int;
  int* b = new int[10]; // new 10个int类型的元素
  int* c = new int(10); // 初始化为10
  delete a;
  delete[] b;
  delete c;
  return 0;
}

newdelete是关键字!!

new在申请自定义类型的空间时,在申请空间后会自动调用其构造函数进行初始化;delete在释放自定义类型的空间时,会先调用它的析构函数,再释放空间。

4. newdelete的实现原理

1. operator newoperator delete函数

newdelete是用户进行动态内存申请和释放的操作符(关键字),而operator newoperator delete是系统提供的全局函数new在底层调用operator new全局函数来动态申请空间,delete在底层通过operator delete全局函数来释放空间。

实际上,operator new 是通过调用malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足时的应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终也是通过调用free来释放空间的。

operator newoperator delete函数对于自定义类型不会调用其构造/析构函数。

2. 重载operator newoperator delete函数

一般情况下不需要对 operator newoperator delete进行重载,除非在申请和释放空间的时候有某些特殊的需求。比如:在使用newdelete申请和释放空间时,打印一些日志信息,或者使用内存池分配空间,不需要operator new直接访问堆。

3. newdelete的实现原理

new:先调用 operator new申请空间(operator new又调用malloc ),然后对于自定义类型会调用它的构造函数初始化。

delete对于自定义类型先调用其析构函数释放资源,然后调用 operator delete函数释放空间(operator delete又调用 free)。

new [N]:先调用 operator new[]申请空间(operator new[]调用operator new ),然后对于自定义类型会调用N次它的构造函数初始化。

delete []:对于自定义类型先调用N次其析构函数释放资源,然后调用 operator delete[]函数释放空间(operator delete[]调用 operator delete)。

5. placement-new

placement-new是指在已分配的动态内存空间中调用构造函数初始化一个对象。 一般是配合内存池使用,因为内存池申请的内存并没有初始化,所以使用placement-new去初始化自定义类型。

使用方式:

class T
{
public:
  T(int a)
    :a_(a)
  {}
  ~T()
  {}
private:
  int a_;
};
int main()
{
  // 正常 new, delete
  T* t = new T(10);
  delete t;
  // 只申请空间,并没有初始化!
  T* t1 = (T*)malloc(sizeof(T));
  assert(t1);
  // 使用 placement-new 初始化 t1
  new(t1)T(20);
  t1->~T();
  free(t1);
  return 0;
}

6. malloc/freenew/delete的区别(面试题)

  1. malloc/new函数调用,而new/delete关键字
  2. new申请空间可以进行初始化。(不过calloc也可以)
  3. malloc要传参指定需要的空间大小,而new只需要给类型就可以。
  4. malloc的返回值是 void*,要进行强转,而new不需要,直接返回对应类型的指针
  5. malloc申请空间失败返回NULL,而**new失败抛异常**。
  6. 对于自定义类型的区别!!

异常捕获:

int main()
{
  try
  {
    // 10G
    int* p = new int[1024*1024*1024*10];
    // ...
  }
  catch(const exception& e)  //  捕获异常信息
  {
    cout << e.what() << endl;  // 打印异常信息
  }
  return 0;
}

7. 内存泄漏

一般有两类内存泄漏:

  1. 动态申请的内存空间没有释放,指向这块内存的指针丢了。
  2. 申请的系统资源没有释放!如文件描述符fd,网络套接字socketfd等。

避免内存泄漏:

  1. 良好的代码规范。
  2. 使用RAII来管理资源。(智能指针)
  3. 使用内存泄漏检测工具。
  4. 动态申请的内存空间没有释放,指向这块内存的指针丢了。
  5. 申请的系统资源没有释放!如文件描述符fd,网络套接字socketfd等。

避免内存泄漏:

  1. 良好的代码规范。
  2. 使用RAII来管理资源。(智能指针)
  3. 使用内存泄漏检测工具。

最后挂个链接,欢迎一起学习,一起进步!https://xxetb.xet.tech/s/4G6TWG

相关文章
|
2月前
|
C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(二)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
2月前
|
编译器 C++ 开发者
【C++】深入解析C/C++内存管理:new与delete的使用及原理(三)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
27天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
66 4
|
2月前
|
存储 程序员 编译器
简述 C、C++程序编译的内存分配情况
在C和C++程序编译过程中,内存被划分为几个区域进行分配:代码区存储常量和执行指令;全局/静态变量区存放全局变量及静态变量;栈区管理函数参数、局部变量等;堆区则用于动态分配内存,由程序员控制释放,共同支撑着程序运行时的数据存储与处理需求。
135 21
|
2月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
2月前
|
存储 C语言 C++
【C++打怪之路Lv6】-- 内存管理
【C++打怪之路Lv6】-- 内存管理
44 0
【C++打怪之路Lv6】-- 内存管理
|
2月前
|
存储 C语言 C++
【C/C++内存管理】——我与C++的不解之缘(六)
【C/C++内存管理】——我与C++的不解之缘(六)
|
2月前
|
程序员 C语言 C++
C++入门5——C/C++动态内存管理(new与delete)
C++入门5——C/C++动态内存管理(new与delete)
72 1
|
2月前
|
编译器 C语言 C++
详解C/C++动态内存函数(malloc、free、calloc、realloc)
详解C/C++动态内存函数(malloc、free、calloc、realloc)
263 1
|
2月前
|
存储 安全 程序员
【C++篇】深入内存迷宫:C/C++ 高效内存管理全揭秘
【C++篇】深入内存迷宫:C/C++ 高效内存管理全揭秘
66 3