【探讨C++内存管理:有效避免内存泄漏与提高性能的关键】(上)

简介: 【探讨C++内存管理:有效避免内存泄漏与提高性能的关键】

【本节目标】


  • 1. C/C++内存分布
  • 2. C语言中动态内存管理方式
  • 3. C++中动态内存管理
  • 4. operator new与operator delete函数
  • 5. new和delete的实现原理
  • 6. 定位new表达式(placement-new)
  • 7. 常见面试题


1. C/C++内存分布


我们先来看一下内存分布图


【说明】:内存划分的意义:不同的数据,有不同的存储需求,各个区域满足不同的需求。

  • 1. 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
  • 2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。
  • 3. 堆用于程序运行时动态内存分配,堆是可以上增长的,应用较多的场景:数据结构
  • 4. 数据段--存储全局数据静态数据
  • 5. 代码段--可执行的代码(经过链接形成的二进制文件)/只读常量,该区域不可被修改。我们平时在vs上写的代码是存在磁盘上的。

  • 强制修改也不行


我们先来看下面的一段代码和相关问题。

#include <iostream>
using namespace std;
int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
  static int staticVar = 1;
  int localVar = 1;
  int num1[10] = { 1, 2, 3, 4 };
  char char2[] = "abcd";
  const char* pChar3 = "abcd";
  int* ptr1 = (int*)malloc(sizeof(int) * 4);
  int* ptr2 = (int*)calloc(4, sizeof(int));
  int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
  free(ptr1);
  free(ptr3);
}



解析:


答案:


静态变量和全局变量有什么区别?


  1. 作用域:
  • 静态变量的作用域限定在声明它的函数内部,即它只在包含它的函数中可见。
  • 全局变量的作用域是整个文件,可以在文件中的任何地方访问。
  1. 生命周期:
  • 静态变量在程序运行期间一直存在,不会因为函数的调用结束而被销毁。它在第一次进入声明它的函数时初始化,然后在程序生命周期内保持其值。
  • 全局变量也在程序运行期间一直存在,它在程序启动时初始化,直到程序结束。


2. C语言中动态内存管理方式:malloc/calloc/realloc/free


void Test()
{
  int* p1 = (int*)malloc(sizeof(int));
  free(p1);
  // 1.malloc/calloc/realloc的区别是什么?
  int* p2 = (int*)calloc(4, sizeof(int));
  int* p3 = (int*)realloc(p2, sizeof(int) * 10);
  // 这里需要free(p2)吗?
  free(p3);
}


  1. malloc(Memory Allocation):


  • 函数原型:void* malloc(size_t size);
  • 用途:用于分配指定大小的内存块,返回一个指向该内存块起始地址的指针。
  • 行为:分配的内存块中的初始内容是未定义的,可能包含任意值。需要注意,malloc 不会初始化分配的内存。


  1. calloc(Contiguous Allocation):


  • 函数原型:void* calloc(size_t num_elements, size_t element_size);
  • 用途:用于分配一块指定数量和大小的连续内存块,返回一个指向该内存块起始地址的指针。
  • 行为:分配的内存块中的每个字节都会被初始化为零。相比于 malloc,calloc 提供了初始化内存的功能,适用于需要确保分配内存中的所有位都为零的情况。


  1. realloc(Reallocate):


  • 函数原型:void* realloc(void* ptr, size_t size);
  • 用途:用于更改之前分配的内存块的大小,可以扩大或缩小。
  • 行为:realloc 可以通过改变原有内存块的大小来满足新的需求。如果扩大内存块,新分配的内存区域的内容是未定义的,而原来的部分会保持不变。如果缩小内存块,多余的部分将会被释放。如果 ptr 是 NULL,realloc 的行为就相当于 malloc(size)。


malloc的实现原理?链接


3. C++内存管理方式


C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因 此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。


3.1 new/delete操作内置类型


void Test()
{
  // 动态申请一个int类型的空间
  int* ptr1 = new int;
  // 动态申请一个int类型的空间并初始化为10
  int* ptr2 = new int(10);
  // 动态申请3个int类型的空间
  int* ptr3 = new int[3];
  // 动态申请3个int类型的空间并初始化为1,2,3
  int* ptr4 = new int[3] {1, 2, 3};
  // 动态申请5个int类型的空间并初始化为1,2,3
  int* ptr5 = new int[5] {1, 2, 3};
  delete ptr1;
  delete ptr2;
  delete[] ptr3;
  delete[] ptr4;
  delete[] ptr5;
}


运行结果:



注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用 new[]和delete[],注意:匹配起来使用。


3.2 new和delete操作自定义类型


class A
{
public:
  A(int a = 0)
    : _a(a)
  {
    cout << "A():" << this << endl;
  }
  ~A()
  {
    cout << "~A():" << this << endl;
  }
private:
  int _a;
};
int main()
{
  // malloc不方便解决动态申请的自定义类型对象的初始化问题
  // new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间\
  还会调用构造函数和析构函数
  // new的本质:开空间+调用构造函数初始化
  // delete的本质:调用析构函数+释放空间
  A* p1 = (A*)malloc(sizeof(A));
  //p1->~A(1);//error C2521: 析构函数 不带任何参数
  A* p2 = new A(1); 
  free(p1);
  delete p2;                    
  // 内置类型是几乎是一样的
  int* p3 = (int*)malloc(sizeof(int)); // C
  int* p4 = new int;
  free(p3);
  delete p4;
  // 多个对象
  A* p5 = (A*)malloc(sizeof(A) * 10);
  free(p5); 
  A aa1(1);
  A aa2(2);
  A aa3(3);
  A* p6 = new A[10]{ aa1, aa2, aa3 };//有名对象
  A* p7 = new A[10]{ A(1),A(2),A(3) };//匿名对象
  A* p8 = new A[10]{ 1 , 2, 3 };//隐式类型转换
  delete[] p6;
  delete[] p7;
  delete[] p8;
  return 0;
}


注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与 free不会。同时我们这里还要注意,malloc开辟失败会返回空指针,而new开辟失败会抛异常。


【探讨C++内存管理:有效避免内存泄漏与提高性能的关键】(下):https://developer.aliyun.com/article/1425600

相关文章
|
3天前
|
算法 Java Python
【Python 的内存管理机制专栏】Python 内存管理实战:性能优化与内存泄漏检测
【5月更文挑战第18天】Python内存管理关乎程序性能与稳定性。优化包括避免过多临时对象,如优化列表推导式减少对象创建。警惕循环引用造成的内存泄漏,如示例中的Node类。使用`gc`模块检测泄漏,通过`gc.set_debug(gc.DEBUG_LEAK)`和`gc.collect()`获取信息。实践中需持续分析内存使用,优化算法、数据结构和资源释放,以提升程序质量与效率。
【Python 的内存管理机制专栏】Python 内存管理实战:性能优化与内存泄漏检测
|
3天前
|
存储 Java 程序员
【Python 的内存管理机制专栏】深入解析 Python 的内存管理机制:从变量到垃圾回收
【5月更文挑战第18天】Python内存管理关乎程序性能与稳定性,包括变量存储和垃圾回收。变量存储时,如`x = 10`,`x`指向内存中值的引用。垃圾回收通过引用计数自动回收无引用对象,防止内存泄漏。了解此机制可优化内存使用,避免循环引用等问题,提升程序效率和稳定性。深入学习内存管理对成为优秀Python程序员至关重要。
【Python 的内存管理机制专栏】深入解析 Python 的内存管理机制:从变量到垃圾回收
|
4天前
|
存储 编译器 Linux
|
6天前
|
存储 编解码 安全
阿里云服务器计算型、通用型、内存型主要实例性能及选择参考
在阿里云的活动中,属于计算型实例规格的云服务器主要有计算型c7、计算型c7a、计算型c8a、计算型c8y、计算型c8i这几个实例规格,属于通用型实例规格的云服务器有通用型g7、通用型g7a、通用型g8a、通用型g8y、通用型g8i,属于内存型实例规格的云服务器有内存型r7、内存型r8a、内存型r8y、内存型r8i等实例。不同实例规格的云服务器在架构、计算、存储、网络、安全等方面有着不同,因此,其适用场景也有所不同。本文来详细介绍一下阿里云服务器计算型、通用型、内存型主要实例计算、存储等性能及其适用场景,以供参考。
阿里云服务器计算型、通用型、内存型主要实例性能及选择参考
|
6天前
|
Go
LabVIEW性能和内存管理 8
LabVIEW性能和内存管理 8
10 0
|
6天前
|
存储 程序员
LabVIEW性能和内存管理 7 LabVIEW中局部和全局变量的内存分配
LabVIEW性能和内存管理 7 LabVIEW中局部和全局变量的内存分配
|
6天前
|
存储
LabVIEW性能和内存管理 6
LabVIEW性能和内存管理 6
10 0
|
6天前
|
测试技术 索引
LabVIEW性能和内存管理 5
LabVIEW性能和内存管理 5
10 0
|
6天前
LabVIEW性能和内存管理 4
LabVIEW性能和内存管理 4
11 0