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

相关文章
|
7天前
|
存储 编译器 C语言
【C++】C\C++内存管理
【C++】C\C++内存管理
【C++】C\C++内存管理
|
5天前
|
编译器 C++
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
|
7天前
|
存储 Java C语言
【C++】C/C++内存管理
【C++】C/C++内存管理
|
12天前
|
存储 编译器 C语言
C++内存管理(区别C语言)深度对比
C++内存管理(区别C语言)深度对比
42 5
|
20小时前
|
存储 程序员 编译器
c++学习笔记08 内存分区、new和delete的用法
C++内存管理的学习笔记08,介绍了内存分区的概念,包括代码区、全局区、堆区和栈区,以及如何在堆区使用`new`和`delete`进行内存分配和释放。
6 0
|
27天前
|
NoSQL Redis C++
c++开发redis module问题之在复杂的Redis模块中,特别是使用第三方库或C++开发时,接管内存统计有哪些困难
c++开发redis module问题之在复杂的Redis模块中,特别是使用第三方库或C++开发时,接管内存统计有哪些困难
|
1月前
|
存储 C语言 C++
【C/C++】动态内存管理( C++:new,delete)
C++的`new`和`delete`用于动态内存管理,分配和释放内存。`new`分配内存并调用构造函数,`delete`释放内存并调用析构函数。`new[]`和`delete[]`分别用于数组分配和释放。不正确匹配可能导致内存泄漏。内置类型分配时不初始化,自定义类型则调用构造/析构。`operator new`和`operator delete`是系统底层的内存管理函数,封装了`malloc`和`free`。定位`new`允许在已分配内存上构造对象,常用于内存池。智能指针等现代C++特性能进一步帮助管理内存。
|
1月前
|
存储 编译器 程序员
【C/C++】动态内存管理(C:malloc,realloc,calloc,free)
探索C++与C语言的动态内存管理:从malloc到new/delete,了解内存分布及栈、堆的区别。文章涵盖malloc、realloc、calloc与free在C中的使用,强调内存泄漏的风险。C++引入new和delete,支持对象构造与析构,还包括operator new和placement-new。深入分析内存管理机制,揭示C与C++在内存处理上的异同。别忘了,正确释放内存至关重要!
|
1月前
|
安全 算法 编译器
C++一分钟之-内存模型与数据竞争
【7月更文挑战第10天】了解C++11内存模型对多线程编程至关重要。它定义了线程间同步规则,包括顺序一致性、原子操作和内存屏障。数据竞争可能导致不确定行为,如脏读和丢失更新。可通过互斥量、原子操作和无锁编程避免竞争。示例展示了`std::mutex`和`std::atomic`的使用。掌握内存模型规则,有效防止数据竞争,确保多线程安全和性能。
35 0
|
7天前
|
存储 算法 搜索推荐
【C++】类的默认成员函数
【C++】类的默认成员函数