C++动态内存管理

在这里插入图片描述

文章目录

  • 🐨0. 前言
  • 🦍1. C/C++内存分布
  • 🦈2. C++内存管理
    • 🐚2.1 new和delete操作内置类型
    • 🐚2.2 new和delete操作自定义类型
  • 🦭3. new和delete底层
    • 🗿3.1 operator new和operator delete函数
    • 🗿3.2 new和delete实现原理
  • 🦨4. 定位new

🐨0. 前言

本篇文章会涉及C语言动态内存结合讲解,如果不是很清楚的铁子,可以查看此篇文章——C语言——动态内存管理

🦍1. C/C++内存分布

在C/C++中,程序中需要存储的数据可分为:局部数据、静态数据、全局数据、常量数据和动态申请数据,这些数据都有其独特的存储位置和生命周期。

  1. 局部数据:

    局部数据是在函数内声明的变量,在函数调用时被创建,函数返回时被销毁,存储在栈区

    void func()
    {int x = 1;//局部数据
    }
    int main()
    {int a = 1;//局部数据return 0;
    }
    
  2. 静态数据:

    静态数据使用static关键字声明,在整个程序生命周期存在,存储在静态区

    static int globalVal = 1; //全局静态数据
    void func()
    {static int x = 1; //局部静态数据
    }
    
  3. 全局数据:

    全局变量是在函数外部声明的变量,具有全局作用域和全局可见性,生命周期直到程序结束,存储在全局数据区

    int globalVal = 1; //全局变量
    void func() 
    {// ...
    }
    
  4. 常量数据:

    常量数据是指在程序中使用字面值或使用 const 修饰符声明的变量,它们的值在程序执行期间保持不变。常量数据存储在只读数据区中,通常与全局数据存储在同一区域

    const int a = 1;
    const char ch = 'a';//常量数据
    void func()
    {// ...
    }
    
  5. 动态申请数据:

    动态申请数据是通过使用 new 运算符(C++)或 malloc() 函数(C)在内存中手动分配的数据。动态申请的数据需要通过 delete 运算符(C++)或 free() 函数(C)显式释放。

    int main()
    {//C++动态内存申请int* p1 = new int;*p1 = 10;delete p1;//C语言动态内存申请int* p2 = (int*)malloc(sizeof(int));if (p2 == nullptr){perror("malloc fail");exit(-1);}*p2 = 10;free(p2);return 0;
    }
    

image-20230627154330850

Tips:

  1. **内核空间:**用于操作系统内核的执行和运行,用户无法直接访问内核空间
  2. **栈:**存放函数调用、局部变量和函数参数等信息,采用后进先出的方式分配和释放内存
  3. **内存映射段:**用于存储被映射到内存的文件和设备等。包括共享库、动态链接库、内存映射文件等
  4. **堆:**用于存储动态申请的内存,在程序运行时根据需要进行分配和释放
  5. **数据段:**存储全局变量、静态变量和静态分配的变量等
  6. **代码段:**存放可执行程序的机器指令,只读常量

🦈2. C++内存管理

C++中,使用newdelete运算符来进行动态内存分配和释放操作。

🐚2.1 new和delete操作内置类型

int main()
{//单个空间申请和释放int* ptr1 = new int;	//动态申请int类型的空间int* ptr2 = new int(10);	//动态申请int类型的空间,并将其初始化为10delete ptr1;delete ptr2;//连续空间申请和释放int* ptr3 = new int[10];	//动态申请10个int类型的空间char* ptr4 = new char[4] {'R', 'M', 'B'};	//动态申请4个char类型的空间,并且初始化delete[] ptr3;delete[] ptr4;return 0;
}

🐚2.2 new和delete操作自定义类型

class A
{
public:A(int a = 1):_a(a){cout << "A(int a = 1)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};
int main()
{//C语言A* p1 = (A*)malloc(sizeof(A));free(p1);//CPPA* p2 = new A(10);delete p2;return 0;
}

C++自定义类型申请动态内存,需要有适当的构造函数和析构函数,以确保对象的正确创建和销毁。分配动态内存分配时,会先调用构造函数,释放时自动调用对象的析构函数。

1.1

🦭3. new和delete底层

🗿3.1 operator new和operator delete函数

newdelete是用来动态申请和释放内存的操作符,而operator newoperator delete是系统提供的全局函数newdelete底层提供调用operator newoperator delete来申请或者释放动态内存。

void* operator new(std::size_t sz)
{std::printf("1) new(size_t), size = %zu\n", sz);if (sz == 0)++sz; if (void *ptr = std::malloc(sz))return ptr;//申请失败抛出异常throw std::bad_alloc{}; 
}void operator delete(void* ptr, std::size_t size) noexcept
{std::printf("4) delete(void*, size_t), size = %zu\n", size);std::free(ptr);
}

来源于C++官网

通过查看operator newoperator delete发现,这两个函数实际上也是通过mallocfree来实现的。只不过operator new相比于malloc,但它申请失败时不是返回空,而是抛出异常(面向过程的语言通常采用抛出异常,而不是返回值)。

image-20230628135101538

🗿3.2 new和delete实现原理

上面提到newdelete是底层是通过调用operator newoperator delete,而operator newoperator delete的底层实现就是mallocfree,说来说去还是mallocfree。对于内置类型的差别并不是很大,而针对于自定义类型,会去调用构造函数和析构函数。

  • new

    1. new首先调用operator new函数进行内存分配。operator new是一个全局函数,通常由编译器提供。它负责分配所需大小的内存块,并返回一个指向分配内存的指针。

    2. 如果分配内存失败,operator new可以抛出std::bad_alloc异常。

    3. new调用构造函数,用于初始化所分配的内存,创建对象。

  • delete

    1. delete首先调用对象的析构函数,用于释放对象占用的资源。
    2. delete调用operator delete函数进行内存释放。operator delete是一个全局函数,通常由编译器提供。它负责释放对象所占用的内存。

image-20230628141815271

malloc / freenew / delete的区别:

  1. mallocfree是函数,而newdelete是操作符

  2. malloc需要手动计算内存块大小,new根据类型自动计算

  3. malloc需要手动进行初始化,new会调用对象的构造函数,自动进行对象的初始化,释放内存时delete会调用析构函数

  4. malloc在分配内存失败时,返回NULL指针,new在分配内存失败时,抛出std::bad_alloc异常,可以通过try-catch块来处理异常情况。

    int main()
    {int* ptr1 = nullptr;try {do{ptr1 = new int[1024 * 1024];cout << ptr1 << endl;} while (ptr1);}catch (const exception& e){cout << e.what() << endl;}return 0;
    }
    

🦨4. 定位new

new是C++的一种扩展形式,它允许在指定的内存地址上创建对象,而不是使用默认的动态内存分配机制。

//定位new的语法:
new (address) Type [initializer];

其中,address是指定的内存地址,Type是要创建的对象类型,initializer是可选的初始化参数。

使用定位new时,编译器不会分配新的内存,而是在指定的地址上构造对象。

class A
{
public:A(int a = 0):_a(a){cout << "A(int a = 0)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};
int main()
{A* p1 = (A*)malloc(sizeof(A));new(p1)A;	//如果类的构造函数有参数时,此处需要传参p1->~A();	//手动调用析构函数free(p1);A* p2 = (A*)operator new(sizeof(A));	//分配内存new(p2)A(10);	//指定地址创建对象p2->~A();operator delete(p2);
}

new通常在特定场景下使用,如在特定内存池、硬件寄存器或内存映射设备上创建对象等。

一般情况下,使用常规的new操作符来动态分配内存和构造对象是更常见和方便的做法。


对于newdelete要知道他们与mallocfree的区别,以及使用动态内存时,需要注意内存泄露。

那本次分享就到这里啦,如果有帮助的话,希望三连支持一下,我们下期再见,如果还有下期的话。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/9057.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2021年国赛高教杯数学建模C题生产企业原材料的订购与运输解题全过程文档及程序

2021年国赛高教杯数学建模 C题 生产企业原材料的订购与运输 原题再现 某建筑和装饰板材的生产企业所用原材料主要是木质纤维和其他植物素纤维材料,总体可分为 A&#xff0c;B&#xff0c;C 三种类型。该企业每年按 48 周安排生产&#xff0c;需要提前制定 24 周的原材料订购和…

Windows Update当前无法检查更新怎么办?

当进行Windows更新或升级时&#xff0c;可能会提示“Windows Update当前无法检查更新&#xff0c;因为未运行服务。您可能需要重新启动计算机”。而当重启也无法解决问题时&#xff0c;我们该怎么办呢&#xff1f;下面我们就来了解一下。 1、删除Software Distribution文件夹中…

基于SpringBoot+SpringCloud+vue的智慧养老平台设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

网络安全:信息收集专总结【社会工程学】

前言 俗话说“渗透的本质也就是信息收集”&#xff0c;信息收集的深度&#xff0c;直接关系到渗透测试的成败&#xff0c;打好信息收集这一基础可以让测试者选择合适和准确的渗透测试攻击方式&#xff0c;缩短渗透测试的时间。 一、思维导图 二、GoogleHacking 1、介绍 利用…

Dockerfile应用的容器化

文章目录 Dockerfile应用的容器化应用的容器化——简介应用的容器化——详解单体应用容器化获取应用代码分析Dockerfile容器化当前应用/构建具体的镜像推送镜像到仓库运行应用程序测试总结 最佳实践利用构建缓存合并镜像 命令总结 Dockerfile应用的容器化 Docker 的核心思想是…

在JDK17尝鲜Flink1.17

在JDK17尝鲜Flink1.17 前言 还没玩明白老版本&#xff0c;Flink1.17就来了&#xff01;&#xff01;&#xff01;总还是要向前看的。。。 根据官网文档&#xff1a;https://nightlies.apache.org/flink/flink-docs-release-1.17/docs/try-flink/local_installation/ Flink r…

Cyclo-(D-Tyr-Gly),1217777-38-2,环-(L-甘氨酰酪氨酸),环二肽(CDPs)作为许多活性天然产物的骨架

Cyclo-(D-Tyr-Gly)中环二肽(CDPs)作为许多活性天然产物的骨架&#xff0c;由于其独特的生物和药理活性等引起了人们的广泛关注。作为环肽化合物&#xff0c;CDPs具有短肽分子良好的生物相容性、低免疫原性等优点。Cyclo-(D-Tyr-Gly)物理参数&#xff1a; CAS号&#xff1a;1217…

Linux高性能网络编程:TCP底层的收发过程

今天探索高性能网络编程&#xff0c;但是我觉得在谈系统API之前可以先讲一些Linux底层的收发包过程&#xff0c;如下这是一个简单的socket编程代码&#xff1a; int main() {... fd socket(AF_INET, SOCKET_STREAM, 0);bind(fd, ...);listen(fd, ...);// 如何建立连接...afd …

ChatGPT | Word文档如何更好地提取表格内容给ChatGPT

本文来自http://blog.csdn.net/hellogv/ &#xff0c;引用必须注明出处&#xff01; Word文档如何更好地提取表格内容给ChatGPT做知识库&#xff0c;这属于文本预处理工作。 本文只讲思路、测试结果&#xff0c;技术实现用Python和Java都能完成&#xff0c;下一篇文章再贴源码…

学会 IDEA 远程 Debug ,直接线上秀操作

有时候我们需要进行远程的debug&#xff0c;本文研究如何进行远程debug&#xff0c;以及使用 IDEA 远程debug的过程中的细节。看完可以解决你的一些疑惑。 配置 远程debug的服务&#xff0c;以springboot微服务为例。 首先&#xff0c;启动springboot需要加上特定的参数。 …

C语言王国探险记之函数的简单概念

王国探险记系列 文章目录&#xff08;5&#xff09; 目录 王国探险记系列 文章目录&#xff08;5&#xff09; 前言 一&#xff0c;函数的基本概念 二&#xff0c;调用外部函数和main()函数区别 2.1如果我们将函数的定义放到后面&#xff0c;可不可以呢&#xff1f; 总结…

liunx安装git

liunx安装git &#xff1a; 提示&#xff1a;记录自己装git 过程 执行下边命令安装 yum -y install git 安装完查看是否安装成功 git --version安装路径默认在/usr/libexe 愉快开始使用git