C++——内存管理

目录

引言

C/C++的内存分布

C语言中动态内存管理方式

C++内存管理方式

1.new/delete操作内置类型

2.new与delete操作自定义类型

operator new与operator delete函数

new与delete的实现

1.内置类型

2.自定义类型

定位new表达式

malloc/free和new/delete的区别

结束语


引言

在简单的学习完类与对象之后,我们接下来学习C++的内存管理。

求点赞收藏评论关注!!!

C/C++的内存分布

我们先来看一段代码以及问题:

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);
}

选择题:

问题:以上数据存放在什么位置?

选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)

1.globalVar 在哪里?                        C
2.staticGlobalVar 在哪里?              C
3. staticVar 在哪里?                        C
4. localVar 在哪里?                         A
5.num1 在哪里?                             A  
6.char2 在哪里?                             A
7.*char2 在哪里?                            A
8. pChar3在哪里?                          A
9.*pChar3 在哪里?                        D
10.ptr1 在哪里?                             A
11.*ptr1 在哪里?                            B

解析:

1.globalVar 是全局变量,全局变量放在静态区。 C

2.staticGlobalVar 也是全局静态变量,放在静态区。与globalVar 的区别是: 普通全局变量作用于整个代码,可被其他文件访问或修改。staticGlobalVar 被 static 修饰,被 static 修饰的静态全局变量只作用于当前文件,其他文件不可见。 C

3.staticVar 是局部静态变量,局部静态变量虽然访问作用域在函数中,但它与全局变量一样,存放在静态区。 C

4.localVar 是局部变量,局部变量放在栈。被static修饰的局部变量的生命周期只会在程序结束后结束,而普通的局部变量的生命周期出了当前作用域就会结束。 A

5.num1 是数组名,是一个局部变量,放在栈区。 A

6.char2 也是一个局部变量,放在栈区,常量字符串"abcd"放在代码段(常量区),数组开辟的空间放在栈区。 A

7.*char2 这里表示的是数组首元素的地址,解引用即表示第一个元素 ‘a’。虽然我们知道常量字符串是存储在常量区,但是,数组中存的 “abcd” 是从常量区中 拷贝 过来存储在数组中,依然是在栈上。 A

8.pChar3 是指针变量,是局部变量,是在栈上开辟空间存储 “abcd” 首字符的地址的变量。 A

9.*pChar3 指向 “abcd” 首元素的地址 ‘a’。“abcd” 是只读常量,放在常量区。 D

10.ptr1 与 pChar3 同理,都是存储在栈上。 A

11.ptr1 指向动态开辟的空间,而 *ptr1 则是问动态开辟出的空间存储在哪,存储在堆上。 B

1.栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。

2.内存映射段是高效的I/0映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。

3.堆用于程序运行时动态内存分配,堆是可以上增长的。

4.数据段--存储全局数据和静态数据,

5.代码段--可执行的代码/只读常量。

C语言中动态内存管理方式

C语言中有关动态内存管理的内容在我的博客:C语言——动态内存管理 中有比较详细的介绍。欢迎各位大佬能阅读一下。

C++内存管理方式

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

1.new/delete操作内置类型

new 运算符用于动态分配内存,并返回指向该内存的指针。它可以与单个对象或对象数组一起使用。

delete 运算符用于释放之前由 new 运算符分配的内存。它必须与 new 运算符成对使用,以避免内存泄漏。

使用示例如下:

int main()
{// 动态分配一个整型变量的内存,并将地址赋给指针p1 int* p1 = new int;// 动态分配一个包含10个整型变量的数组的内存,// 并将数组首地址赋给指针p2int* p2 = new int[10];// 注意:这里的数组也是未初始化的,数组中的所有元素都是未定义的。// 动态分配一个整型变量的内存,并初始化为10,然后将地址赋给指针p3int* p3 = new int(10);// 这里,p3指向的整型变量被明确初始化为10。//动态申请5个int类型空间,并将前面2个初始化为1,后面默认初始化为0int* p4 = new int[5] {1, 1};//与C语言free功能类似,释放空间防止内存泄漏delete p1;delete[] p2;delete p3;delete[] p4;return 0;
}

我们可以通过监视窗口来观察一下:

2.new与delete操作自定义类型

使用new和delete来操作内置类型(如int、float、char等)时,它们在底层的行为上与C语言中的malloc和free没有太大的本质区别。这主要体现在它们都是用来在堆上分配和释放内存的。然而,它们之间还是存在一些关键的区别和考虑因素,尤其是在处理自定义类型时。

来看看这段代码:

class A
{
public:A(int a = 0):_a(a){cout << "A()" << endl;}~A(){cout << "~A()" << endl;}void Print(){cout << "_a = " << _a << endl;}
private:int _a = 0;
};int main()
{A* p1 = new A;A* p2 = new A(10);p1->Print();p2->Print();delete p1;delete p2;return 0;
}

运行结果为:

我们可以得知:new在创建自定义类型时会自动调用其构造函数,delete在释放其空间时会自动调用其析构函数。

operator new与operator delete函数

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

下面是operator new与operator delete函数的源代码:

void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{// try to allocate size bytesvoid* p;while ((p = malloc(size)) == 0)if (_callnewh(size) == 0){// report no memory// 如果申请内存失败了,这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void* pUserData)
{_CrtMemBlockHeader* pHead;RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));if (pUserData == NULL)return;_mlock(_HEAP_LOCK); /* block other threads */__TRY/* get a pointer to memory block header */pHead = pHdr(pUserData);/* verify block type */_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));_free_dbg(pUserData, pHead->nBlockUse);__FINALLY_munlock(_HEAP_LOCK); /* release other threads */__END_TRY_FINALLYreturn;
}

在C++中,operator new 和 operator delete 是全局函数,它们分别用于内存的申请和释放。这些函数在内部通常会使用C语言风格的内存管理函数(如malloc和free)来执行实际的内存分配和释放操作,但它们提供了额外的功能,特别是异常处理和类型安全。

operator new可以配置为在内存分配失败时抛出一个std::bad_alloc异常,而不是简单地返回nullptr。这允许C++程序以一种更加面向对象和异常安全的方式来处理内存分配失败的情况。

operator delete 最终是通过free来释放空间的。

new与delete的实现

接下来我们来探讨一下new与delete的实现,来看一下这段代码:

class A
{
public:A(){cout << "A()" << endl;}~A(){cout << "~A()" << endl;}
private:int _x = 0;
};void Test()
{int* ptr1 = new int;	//内置类型A* ptr2 = new A;		//自定义类型delete ptr1;delete ptr2;
}

我们通过反汇编语言来观察一下:

1.内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:

new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。

2.自定义类型

new的原理:
(1)调用operator new函数申请空间
(2)在申请的空间上执行构造函数,完成对象的构造

delete的原理:
(1)在空间上执行析构函数,完成对象中资源的清理工作
(2)调用operator delete函数释放对象的空间

new T[N]的原理:
(1)调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
(2)在申请的空间上执行N次构造函数

delete[]的原理:
(1)在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
(2)调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

定位new表达式

定位new表达式(placement new)是一种特殊的C++语法,允许开发者在已分配但未经初始化的内存区域上构造对象。

定位new的基本使用格式如下:

new (place_address) type;

或者,如果需要传递初始化列表给构造函数,可以这样做:

new (place_address) type(initializer-list);

place_address 是一个指向足够大且已分配(但可能未初始化)的内存块的指针,用于存储新构造的对象。

type 是要构造的对象的类型。

initializer-list 是一个可选的初始化列表,用于传递给对象的构造函数。

下面是个简单的使用示例:

class A 
{
public:A(int a = 0) : _a(a)  // 使用初始化列表显式初始化成员变量_a {cout << "A(int a = 0):" << _a << endl;}// 析构函数~A(){cout << "~A()\n";}
private:int _a;
};int main() 
{// 分配内存    void* memory = operator new(sizeof(A));// 使用定位new构造对象:在已分配的内存上构造A类的对象A* a = new (memory) A;// 显式调用析构函数    a->~A();// 释放内存    operator delete(memory);return 0;
}

输出结果为:

malloc/free和new/delete的区别

(1)malloc和free是函数,new和delete是操作符。
(2)malloc申请的空间不会初始化,new可以初始化。
(3)malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可。
(4)malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型。
(5)malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常。
(6)申请自定义类型对象时,malloc / free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理。

结束语

写的有点墨迹了。。。

求点赞收藏评论关注!!!

感谢各位大佬支持!!!

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

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

相关文章

关于Spring Cloud Gateway中 Filters的理解

Spring Cloud Gateway中 Filters的理解 Filters Filters拦截器的作用是&#xff0c;对请求进行处理 可以进行流量染色 ⭐增加请求头 例子 spring:cloud:gateway:routes:- id: add_request_header_routeuri: http://localhost:8123predicates:- Path/api/**filters:- AddR…

Redis的缓存穿透、缓存雪崩、缓存击穿怎么解决

Redis在实际使用中是会遇到很多问题的&#xff0c;例如今天说到的缓存穿透、缓存雪崩、缓存击穿。 缓存穿透&#xff1a; 缓存穿透是指客户端请求的数据在redis缓存和数据中都不存在&#xff0c;这样缓存永远都不会生效&#xff0c;这些请求都会打到数据库当中&#xff0c;对…

MySQL_数据类型简介

课 程 推 荐我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448;入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448;虚 拟 环 境 搭 建 &#xff1a;&#x1…

Setting Design Properties

设置设计属性 接下来&#xff0c;在设计上设置配置模式。这是导致物理 约束&#xff0c;在这种情况下是设计的属性&#xff0c;而不是单元的属性。首先&#xff0c;列出所有 当前设计的特性。 1.在Tcl控制台中列出设计的属性&#xff1a; list_property [current_design] 此命…

ITOP-2 分模块安装部署itop

ITOP-2 分模块安装部署itop 一、安装PHP组件1、查看当前Linux服务器安装的PHP版本2、安装源epel&#xff0c;安装源remi&#xff0c;安装yum-config-manager3、用yum-config-manager指定remi的php7.2仓库4、安装升级php5、验证当前PHP的版本 二、部署 MySQL 服务1、设置 Repo2、…

基于TRIZ的救援机器人轻量化设计

在救援机器人设计中&#xff0c;轻量化是一个至关重要的目标&#xff0c;它直接关系到机器人的便携性、运输效率以及在复杂环境中的作业能力。TRIZ理论为我们提供了一套系统化的工具和方法&#xff0c;用于解决设计过程中遇到的各种挑战&#xff0c;特别是在实现轻量化目标时&a…

论文阅读-Demystifying Misconceptions in Social Bots Research

论文链接&#xff1a; https://arxiv.org/pdf/2303.17251 目录 摘要: Introduction Methodological issues Information leakage Cherry-picking&#xff08;采摘樱桃&#xff09; Straw-man methodology &#xff08;稻草人&#xff09; Data biases Conceptual issu…

基于ssm+vue+uniapp的智能停车场管理系统小程序

开发语言&#xff1a;Java框架&#xff1a;ssmuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;M…

vue2基础系列教程之todo的实现及面试高频问题

关键知识点 v2里面&#xff0c;当在同一个元素或组件上同时使用v-for和v-if,v-for的权限高于v-if v-show和v-if的区别主要有 v-if是惰性的&#xff0c;v-show是及时的v-if值为false时&#xff0c;不会生成dom,v-show不管值是true或false,都会生成dom,修改的是dom的display属性…

传知代码-KAN卷积:医学图像分割新前沿

代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 概述 在本文中深入探讨KAN卷积在医学图像分割领域的创新应用&#xff0c;特别是通过引入Tokenized KAN Block&#xff08;Tok Kan&#xff09;这一突破性设计&#xff0c;将深度学习中的图像分割技术推向了新的高…

轮转数组 给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数

示例 1: 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6]向右轮转 2 步: [6,7,1,2,3,4,5] 向右轮转 3 步: [5,6,7,1,2,3,4]示例 2: 输入&#xff1a;nums [-1,-100,3,99], k 2 输出&#xff1a;[3,99,-1,-100] 解释: 向右…

Halo 开发者指南——项目运行、构建

准备工作 环境要求 OpenJDK 17 LTSNode.js 20 LTSpnpm 9IntelliJ IDEAGitDocker&#xff08;可选&#xff09; 名词解释 工作目录 指 Halo 所依赖的工作目录&#xff0c;在 Halo 运行的时候会在系统当前用户目录下产生一个 halo-next 的文件夹&#xff0c;绝对路径为 ~/ha…

通过LiveGBS实现安防监控摄像头GB28181转成WebRTC流实现web浏览器网页无插件低延迟直播...

目录 1、WebRTC超低延时直播2、WebRTC延时对比3、LiveGBS的低延时的WebRTC流4、分屏页面如何选择默认播放流5、无法播放Webrtc6、搭建GB28181视频直播平台 1、WebRTC超低延时直播 需要低延时的视频流监控播放&#xff0c;之前可以用rtmp的低延时播放(1秒左右)&#xff0c;随着浏…

腾讯百度阿里华为常见算法面试题TOP100(4):双指针、哈希、滑动窗口

之前总结过字节跳动TOP50算法面试题&#xff1a; 字节跳动常见算法面试题top50整理_沉迷单车的追风少年-CSDN博客_字节算法面试题 目录 双指针 42.接雨水 283.移动零 11.盛最多水的容器 15.三数之和 哈希 1. 两数之和 49.字母异位词分组 128.最长连续序列 滑动窗…

GUI编程13:JDialog弹窗

视频链接&#xff1a;15、JDialog弹窗_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1DJ411B75F?p15&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 package com.yundait.lesson04;import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; im…

计算机网络27、28——Linux命令1、2

1、虚拟机网络前方路径内容 用户名机器名&#xff1a;/$ $表示普通用户&#xff0c;#表示root用户 2、Linux不分盘&#xff0c;都是绝对路径 /表示根目录&#xff0c;表示计算机文件夹下 ~是当前用户的家&#xff0c;表示home文件夹下自己的文件夹 3、bin文件夹下的是可执…

书生大模型全链路开源体系,学习

优点 书生浦语开源大模型&#xff0c;是一个开源的大模型&#xff0c;大家可以一起学习 还有配套的教学视频&#xff0c;很快就能上手&#xff0c;而且还奖励算力&#xff0c;可以直接训练&#xff0c;讨论学习&#xff0c;非常nice。 教学视频 书生浦语大模型全链路开源开…

Qt实现登录界面

本文基于Qt实现一个简单的登录界面&#xff0c;主要使用到Widget、button、edit等控件&#xff0c;基于自定义的信号槽实现界面的跳转&#xff0c;使用绘图设备添加背景图等。 1. 创建主界面 设计主界面的样式&#xff0c;并添加相关的控件。如下显示&#xff1a; 代码如下&…

Android Tools | 如何使用Draw.io助力Android开发:从UI设计到流程优化

Android Tools | 如何使用Draw.io助力Android开发&#xff1a;从UI设计到流程优化 1. 引言 在Android开发中&#xff0c;视觉化设计与流程管理至关重要。虽然开发工具如Android Studio强大&#xff0c;但它并不适用于所有设计场景。Draw.io是一款免费的在线绘图工具&#xff…

【C++笔记】类和对象的深入理解(三)

【C笔记】类和对象的深入理解(三) &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;C笔记 文章目录 【C笔记】类和对象的深入理解(三)前言一.日期类的实现1.1声明和定义分离1.2日期类整数1.3日期类整数1.4日期类-整数1.5日期类-日期1.6复用对…