C/C++ - 内存管理(C++)

堆栈

  • C++中的栈和堆是用于存储变量和对象的两个主要内存区域。

  • 栈是一种自动分配和释放内存的区域,用于存储局部变量和函数调用的上下文。栈上的内存分配和释放是自动进行的,无需手动管理。

  • 堆是动态分配内存的区域,用于存储动态创建的对象和数据结构。

  • 堆上的内存分配和释放需要手动进行,通过使用 new / malloc 和 delete / free 运算符或者使用智能指针等机制。

  • 堆(Heap)

    • 堆是在程序运行时动态分配内存的区域,用于存储动态创建的对象和数据结构。

    • 在堆上分配的内存需要手动进行管理,通过使用 new​​​ 和 delete​​​ 运算符或者智能指针​​等机制进行内存的分配和释放。

    • 堆上的内存可以在任何时候进行分配和释放,而不受作用域的限制。

      cpp 复制代码
      int* ptr = new int;  // 动态分配一个整型内存块
      *ptr = 5;
      delete ptr;  // 释放内存
  • 栈(tack)

    • 栈是基于线程而言的,每条线程都有属于自己的栈区。

    • 栈是一种自动分配和释放内存的区域,用于存储局部变量和函数调用的上下文。

    • 栈上的内存分配和释放是自动进行的,无需手动管理。

    • 栈上的内存分配和释放遵循"先进后出"的原则,即最后进入栈的变量最先离开。

      cpp 复制代码
      void foo() 
      {
          int x = 5;  // 在栈上分配整型变量
          // ...
      }  // 函数结束,栈上的变量自动释放
  • 栈上分配的内存特点

    • 栈上分配的内存空间相对较小,受限于编译器和操作系统的设置。通常在几兆字节到几十兆字节之间。
    • 栈上的内存分配和释放速度较快,仅涉及移动栈指针。
    • 栈上的内存分配是按照严格的顺序进行的,无法随机访问。
  • 堆和栈的比较

    • 堆和栈都是用于存储数据的内存区域,但它们有不同的特点和用途。
    • 堆适用于动态分配内存,可以在任何时候进行分配和释放,适用于需要灵活管理内存的情况。
    • 栈适用于自动分配和释放内存,适用于局部变量和函数调用的上下文。

内存

  • new

    • 动态分配单个对象

      • 使用new​​​​运算符可以在堆上动态分配单个对象的内存,并返回指向该内存的指针。

      • 语法:new 类名;​​​​ 或 new 类名(参数);​​​​

        cpp 复制代码
        int* ptr = new int;  // 动态分配一个整型对象
        *ptr = 5;  // 对分配的内存进行操作
        delete ptr;  // 释放内存
    • 底层执行

    • 动态分配对象数组:

      • 使用new​​​​运算符可以在堆上动态分配对象数组的内存,并返回指向该内存的指针。

      • 语法:new 类名[数组大小];​​​​

        cpp 复制代码
        int* arr = new int[5];  // 动态分配一个包含5个整型元素的数组
        for (int i = 0; i < 5; ++i) 
        {
            arr[i] = i;
        }
        delete[] arr;  // 释放内存
  • delete

    • 释放单个对象内存:

      • 使用delete​​运算符可以释放通过new​​运算符分配的单个对象的内存。

      • 语法:delete 指针;​​

        cpp 复制代码
        int* ptr = new int;  // 动态分配一个整型对象
        *ptr = 5;
        delete ptr;  // 释放内存
    • 释放对象数组内存:

      • 使用delete[]​​运算符可以释放通过new​​运算符分配的对象数组的内存。

      • 语法:delete[] 指针;​​

        cpp 复制代码
        #include <iostream>
        
        
        int main()
        {
        	//new    - malloc
        	//delete - free
        
        	//C
        	int* p1 = (int*)malloc(sizeof(int));
        	if (p1)
        	{
        		free(p1);
        		p1 = NULL;
        	}
        
        	int* p2 = (int*)malloc(sizeof(int) * 10);
        	if (p2)
        	{
        		free(p2);
        		p2 = NULL;
        	}
        
        	//CPP
        	int* p3 = new int;
        	if (p3)
        	{
        		delete p3;
        		p3 = NULL;
        	}
        
        	int* p4 = new int(10);
        	if (p4)
        	{
        		delete p4;
        		p4 = NULL;
        	}
        
        	int* p5 = new int[10];
        	if (p5)
        	{
        		delete[] p5;
        		p5 = NULL;
        	}
        
        	return 0;
        }
  • 内存失败处理

    • 在使用new​​运算符分配内存时,如果内存不足或分配失败,会抛出std::bad_alloc​​异常。因此,需要在代码中适当处理异常情况

    • 可以使用try-catch​​语句块来捕获并处理异常。

      cpp 复制代码
      #include <iostream>
      
      
      void Exception_CPP()
      {
      	try
      	{
      		//可能会出现错误的代码
      		long long* p = new long long[0xFFFFFFF];
      		delete[] p;
      	}
      	catch (const std::exception& Error)
      	{
      		//出现异常捕获处理异常
      		std::cout << "Exception ->" << Error.what() << std::endl;
      	}
      
      }
      
      int main()
      {
      	Exception_CPP();
      
      	return 0;
      }

智能指针

  • 在C++中,智能指针是一种用于管理动态分配的内存资源的工具。它们以对象的形式封装了原始指针,并提供了自动化的内存管理和资源释放,从而减少内存泄漏和悬挂指针等问题。
  • 智能指针主要有两种类型:shared_ptr和unique_ptr。
  • ​shared_ptr

    • ​shared_ptr​​是一种引用计数智能指针,用于多个指针共享同一个对象。它会跟踪有多少个shared_ptr​​指向同一块内存,并在不再需要时自动释放该内存。

    • 创建

      cpp 复制代码
      std::shared_ptr<int> ptr = std::make_shared<int>(42);
    • 引用计数

      cpp 复制代码
      std::shared_ptr<int> ptr1 = std::make_shared<int>(42);
      std::shared_ptr<int> ptr2 = ptr1; // 引用计数递增
      
      std::cout << ptr1.use_count() << std::endl; // 输出2,引用计数为2
    • 解引用

      cpp 复制代码
      std::shared_ptr<int> ptr = std::make_shared<int>(42);
      std::cout << *ptr << std::endl; // 输出42
    • 重置指针

      cpp 复制代码
      std::shared_ptr<int> ptr = std::make_shared<int>(42);
      ptr.reset(); // 释放资源,引用计数减少
      
      if (ptr == nullptr) {
          std::cout << "智能指针为空" << std::endl;
      }
  • ​​unique_ptr

    • ​​unique_ptr​​是一种独占式智能指针,用于唯一地拥有一个对象。它提供了对动态分配的内存的所有权,并在不再需要时自动释放该内存。

    • 创建

      cpp 复制代码
      std::unique_ptr<int> ptr = std::make_unique<int>(42);
    • ​​移动语义​

      cpp 复制代码
      std::unique_ptr<int> ptr1 = std::make_unique<int>(42);
      std::unique_ptr<int> ptr2 = std::move(ptr1); // 移动语义,ptr1不再拥有资源
      
      if (ptr1 == nullptr) 
      {
          std::cout << "ptr1不再拥有资源" << std::endl;
      }
    • 解引用

      cpp 复制代码
      std::unique_ptr<int> ptr = std::make_unique<int>(42);
      std::cout << *ptr << std::endl; // 输出42
    • 释放资源

      cpp 复制代码
      std::unique_ptr<int> ptr = std::make_unique<int>(42);
      ptr.release(); // 释放资源,但不销毁指针
      
      if (ptr == nullptr) 
      {
          std::cout << "unique_ptr已释放资源" << std::endl;
      }
相关推荐
西岭千秋雪_1 分钟前
设计模式の中介者&发布订阅&备忘录模式
java·观察者模式·设计模式·中介者模式·备忘录模式
捕鲸叉2 分钟前
C++软件设计模式之代理(Proxy)模式
c++·设计模式
憶巷8 分钟前
MyBatis中动态SQL执行原理
java·sql·mybatis
重生之绝世牛码8 分钟前
Java设计模式 —— 【结构型模式】享元模式(Flyweight Pattern) 详解
java·大数据·开发语言·设计模式·享元模式·设计原则
seasugar14 分钟前
记一次Maven拉不了包的问题
java·maven
Allen Bright22 分钟前
【Java基础-26.1】Java中的方法重载与方法重写:区别与使用场景
java·开发语言
苹果酱056724 分钟前
Golang的文件解压技术研究与应用案例
java·vue.js·spring boot·mysql·课程设计
秀儿y27 分钟前
单机服务和微服务
java·开发语言·微服务·云原生·架构
ybq1951334543128 分钟前
javaEE-多线程案例-单例模式
java·开发语言
code monkey.33 分钟前
【排序算法】—— 计数排序
c++·算法·排序算法