【c++篇】掌握动态内存的奥妙
【C++篇】动态内存
- 一、Static 关键字
- 1.1函数内部的静态变量
- 1.2 全局静态变量
- 1.3静态成员变量
- 1.4静态成员函数
- 二、内存管理
- 2.1栈区(Stack)
- 2.2堆区(Heap)
- 三、动态内存分配机制
- 3.1、动态内存分配的两种方法
- c语言
- c++
- 3.2new 和delete的用法
- 3.3语法和类型安全性
- 3.4new和delete实现原理
- 3.4.1内置类型
- 3.4.2自定义类型
- 四、operator new与operator delete函数
- 4.1 operator new
- 4.2 operator delete
- 4.3默认实现与工作原理
- 4.4operator new 和 operator delete 的重载
- 4.5 malloc/free和new/delete的区别
链接: 基于上篇我们学习了构造函数、重载等,我们继续探究类与对象以及内存管理 https://blog.csdn.net/2401_83157962/article/details/143778695
一、Static 关键字
定义:
- 静态数据成员:属于类本身,而不是类的某个对象。它在整个程序中只有一份拷贝,所有对象共享。
- 静态成员函数:不依赖具体对象调用,不能访问非静态成员。
特点:
- 静态数据成员在程序启动时分配内存,直到程序结束时释放。
- 静态成员函数不能使用 this 指针,也不能直接访问非静态成员变量或成员函数。
1.1函数内部的静态变量
当在函数内部声明一个变量为static时,这个变量的生命周期会延续到程序结束,但它的作用域仅限于该函数。换句话说,虽然它只能在定义它的函数内访问,但它不会在函数调用结束后被销毁。
示例如下:
#include <iostream> void counter() { static int count = 0; // 静态变量只会被初始化一次 count++; std::cout << "Count: " << count << std::endl;
} int main() { counter(); // 输出 Count: 1 counter(); // 输出 Count: 2 counter(); // 输出 Count: 3 return 0;
}
1.2 全局静态变量
如果在文件级别(全局作用域)定义一个静态变量,它只能在当前文件内访问。这对于实现模块封装非常有用,可以防止命名冲突。
示例如下:
下面展示一些 内联代码片。
#include <iostream> static int globalVariable = 42; // 只能在此文件中使用 void display() { std::cout << "Global variable: " << globalVariable << std::endl;
} int main() { display(); return 0;
}
1.3静态成员变量
在类中,使用static可以声明静态成员变量,它属于类而不是某个特定的对象。所有对象共享这个变量,静态成员变量在类的所有对象之间是相同的。
示例如下:
#include <iostream>
class MyClass {
public: static int staticValue; // 声明静态成员变量 MyClass() { staticValue++; }
}; int MyClass::staticValue = 0; // 定义并初始化静态成员变量 int main() { MyClass obj1; MyClass obj2; std::cout << "Static Value: " << MyClass::staticValue << std::endl; // 输出 2 return 0;
}
1.4静态成员函数
静态成员函数是属于类的,而不是类的实例。它们只能访问静态成员变量或静态成员函数,不能访问非静态的成员变量和成员函数
示例如下:
#include <iostream> class MyClass {
public: static int staticValue; static void display() { // 静态成员函数 std::cout << "Static Value: " << staticValue << std::endl; }
}; int MyClass::staticValue = 10; int main() { MyClass::display(); // 通过类名访问静态成员函数 return 0;
}
二、内存管理
2.1栈区(Stack)
栈内存是由编译器自动管理的。函数调用时,局部变量会在栈上分配内存,当函数返回后,分配的内存会自动被释放。由于栈内存的这种自动管理特性,使用栈内存的分配和释放非常高效,但栈内存的大小是有限的,通常适合存储小型对象或简单的数据结构。
特点:
- 为局部变量和函数调用分配内存,生命周期由作用域决定。
- 自动分配和释放,速度快,但大小有限。
示例如下:
void stackExample() {int a = 10; // 栈分配
} // 离开作用域后,a 自动销毁
2.2堆区(Heap)
定义:
堆内存是由程序员手动管理的。通过new和delete运算符进行动态内存分配和释放。这种方法允许在运行时进行内存分配,因此可以根据需要分配更大的内存空间,但同时也增加了内存泄漏和碎片化的风险。
特性:
- 由程序员手动分配和释放,灵活性高,但容易出现内存泄漏。
- 使用 new 和 delete 管理。
示例如下:
int* p = new int(10); // 堆分配
delete p; // 手动释放
三、动态内存分配机制
动态内存分配是通过new和delete操作符完成的。new运算符用于在堆上分配内存,并返回指向分配的内存的指针;delete运算符则用于释放之前使用new分配的内存。
3.1、动态内存分配的两种方法
c语言
- 在C语言中,动态内存分配主要通过标准库函数(malloc、calloc、realloc、free)来完成。C语言不提供内置的类型安全机制,因此程序员需要手动管理内存的分配和释放。
int* arr = (int*)malloc(10 * sizeof(int)); // 分配内存
free(arr); // 释放内存
c++
在C++中,动态内存分配主要通过new和delete运算符来完成。与C语言不同,C++的内存管理提供了更丰富的语法,使得代码更加类型安全,并且支持构造和析构函数的调用。
示例如下:
int* arr = new int[10]; // 分配内存
delete[] arr; // 释放内存
3.2new 和delete的用法
- 分配单个对象:
int* p = new int(42); // 分配并初始化
delete p; // 释放内存
- 分配数组
int* arr = new int[10]; // 分配数组
delete[] arr; // 释放数组
3.3语法和类型安全性
-
C语言:使用malloc和free进行内存分配和释放。由于返回的是void*类型的指针,常常需要进行强制类型转换,缺乏类型安全保障。
-
C++:使用new和delete,内存分配后已经是目标类型,无需进行转换。此外,使用new时会调用对象的构造函数,delete会调用析构函数,这对于管理对象资源非常有用。
3.4new和delete实现原理
3.4.1内置类型
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常, malloc会返回NULL。
3.4.2自定义类型
- new 原理
- 调用operator new函数申请空间
- 在申请的空间上执行构造函数,完成对象的构造
- delete原理
- 在空间上执行析构函数,完成对象中资源的清理工作
- 调用operator delete函数释放对象的空间
- new T[N] 原理
- 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
- 在申请的空间上执行N次构造函数
- delete[ ] 原理
- 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
- 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间
四、operator new与operator delete函数
4.1 operator new
定义:
operator new 是 C++ 中用于分配内存的运算符,用于申请指定数量的内存块。与 C 语言的 malloc 相比,operator new 提供了一种类型安全的方式来分配内存,因为它返回的是指定类型的指针,而不是 void*。
特性·:
- 是 C++ 的内存分配函数,类似于 C 中的 malloc。
- 被 new 关键字调用,用于在堆上分配足够的内存以容纳一个或多个对象。
- 默认实现使用标准库的 malloc 来分配内存。
- 如果分配失败,会抛出 std::bad_alloc 异常,而不是返回 nullptr。
语法:
void* operator new(std::size_t size);
- size:要分配的字节数。
- 返回值:返回一个指向已分配内存的指针,如果分配失败,将会抛出std::bad_alloc异常。
4.2 operator delete
operator delete 是与 operator new 配对使用的函数,用于释放之前分配的内存。它的基本形式如下:
void operator delete(void* ptr);
- ptr:指向要释放的内存块的指针
特性:
- 是 C++ 的内存释放函数,类似于 C 中的 free。
- 被 delete 关键字调用,用于释放 operator new 分配的内存。
- 默认实现使用标准库的 free 来释放内存。
4.3默认实现与工作原理
operator new 的作用是分配一块大小为 size 的内存(以字节为单位),并返回指向分配内存的指针。它的默认实现可以表示如下:
void* operator new(size_t size) {if (size == 0) size = 1; // 确保最小分配单位void* ptr = malloc(size); // 调用 C 的 mallocif (!ptr) throw std::bad_alloc(); // 分配失败抛异常return ptr;
}
分配行为:
- 如果 size 为 0,operator new 会分配一个最小的非零内存块。
- 使用 malloc 从堆上分配内存。
- 如果 malloc 返回 nullptr(分配失败),operator new 会抛出异常。
operator delete 的作用是释放 operator new 分配的内存块。它的默认实现如下:
void operator delete(void* ptr) noexcept {free(ptr); // 调用 C 的 free
}
释放行为:
- 如果传入的 ptr 是 nullptr,operator delete 不会执行任何操作。
- 否则,它调用 free 将内存归还给系统。
4.4operator new 和 operator delete 的重载
C++ 允许重载这些操作符,以便提供自定义内存管理策略。重载可以帮助跟踪内存分配、统计内存使用、实现自定义的内存池等。
示例如下:
#include <iostream>
#include <cstdlib> void* operator new(std::size_t size) { std::cout << "Allocating " << size << " bytes\n"; void* p = std::malloc(size); if (!p) throw std::bad_alloc(); return p;
} void operator delete(void* ptr) noexcept { std::cout << "Deallocating memory\n"; std::free(ptr);
} int main() { int* p = new int(10); // Calls overloaded operator new std::cout << "Value: " << *p << std::endl; delete p; // Calls overloaded operator delete return 0;
}
在这个示例中,重载的 operator new 在每次内存分配时打印分配的字节数,而 operator delete 则在释放内存时打印相应的信息。
4.5 malloc/free和new/delete的区别
malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:
- malloc和free是函数,new和delete是操作符
- malloc申请的空间不会初始化,new可以初始化
- malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可, 如果是多个 对象,[]中指定对象个数即可
- malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
- malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
- 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间 后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理
最后
- operator new 和 operator delete 是 C++ 提供的用于动态内存管理的操作符,提供了一种类型安全的方式来分配和释放内存
- 始终确保内存分配和释放对称,避免内存泄漏和悬垂指针(指向已释放内存的指针称为悬垂指针,访问悬垂指针会导致未定义行为。)。
- 推荐使用 智能指针,如 std::unique_ptr 和 std::shared_ptr,自动管理动态
相关文章:
【c++篇】掌握动态内存的奥妙
【C篇】动态内存 一、Static 关键字1.1函数内部的静态变量1.2 全局静态变量1.3静态成员变量1.4静态成员函数 二、内存管理2.1栈区(Stack)2.2堆区(Heap) 三、动态内存分配机制3.1、动态内存分配的两种方法c语言c 3.2new 和delete的用法3.3语法和类型安全性…...
5.4.2-3 编写Java程序读取HDFS文件
在本次实战中,我们通过Java程序实现了从Hadoop分布式文件系统(HDFS)读取文件的功能。首先,我们创建了ReadFileOnHDFS类,并在其中实现了两个方法:read1()和read1_()。read1()方法展示了如何打开HDFS文件并逐…...
@EnableConfigurationProperties @ConfigurationProperties
EnableConfigurationProperties && ConfigurationProperties的使用时机 今天在写properties时想到了这个问题,为什么有时候我需要写EnableConfigurationProperties有时候又不需要呢?下面就详细讲讲。 Data Component ConfigurationProperties(pr…...
RK3588适配MTK7921 USB接口WiFi驱动开发
在当前RK原厂提供的SDK里面已经适配的WiFi模组有不少,但是支持的模组大部分集中在realtek、正基、英飞凌等厂家。主要型号有Realtek的RTL8188系列、RTL8723系列、RTL8812系列、RTL8821系列、RTL8822系列和支持WiFi 6 的RTL8852系列,正基的AP6275系列、AP6276系列等。接下来将…...
【数据结构OJ】【图论】图综合练习--拓扑排序
题目描述 已知有向图,顶点从0开始编号,求它的求拓扑有序序列。 拓扑排序算法:给出有向图邻接矩阵 1.逐列扫描矩阵,找出入度为0且编号最小的顶点v 2.输出v,并标识v已访问 3.把矩阵第v行全清0 重复上述步骤࿰…...
模型 I/O 与 LangChain 实践
模型 I/O 与 LangChain 实践 本文是《LangChain 实战课》第 4 节——模型 I/O:输入提示、调用模型、解析输出的一些学习笔记与总结。这篇文章将围绕模型 I/O 的基本概念、LangChain 提供的最佳实践以及如何通过 LangChain 实现高效的结构化数据处理展开。 什么是模…...
C++:用红黑树封装map与set-1
文章目录 前言一、STL源码分析二、红黑树的构建三、map与set整体框架的搭建与解析四、如何取出进行比较?1. met与set的数据是不同的2. 取出数据进行比较1)问题发现2)仿函数解决 五、封装插入六、迭代器的实现1. operator* 与operator->2. …...
HBU算法设计与分析 贪心算法
1.最优会场调度 #include <bits/stdc.h> using namespace std; const int N1e55; typedef pair<int,int> PII; PII p[N]; priority_queue<int,vector<int>,greater<int>> q; //最小堆 存储最早结束的会场的结束时间 int n; //其实这个题可以理…...
python pycharm安装教程及基本使用,超详细
一.PyCharm下载及安装 1.1 进入pycharm官网,点击下载,下载社区版本(日常学习使用够用了),专业版是收费的哦(功能更强大) Download PyCharm: The Python IDE for data science and web development by Jet…...
变量提升函数提升
示例 1:变量提升 原始代码: console.log(x); // 输出: undefined var x 5; console.log(x); // 输出: 5提升后的代码(理解为): var x; // 变量声明被提升 console.log(x); // 输出: undefined x 5; // 赋值 conso…...
el-table vue3统计计算数字
固定合计在最下列 父组件 <template><el-tablev-loading"loading"tooltip-effect"light":data"list"style"width: 100%":max-height"maxHeight"element-loading-text"拼命加载中...":header-cell-styl…...
IDE应当具备的功能
IDE 是辅助编程的工具,应当具备以下功能 语法高亮 显示注释 显示光键词 显示括号 matlab 自带的 IDE 没有这个功能 显示缩进 matlab 自带的 IDE 没有这个功能 显示字符串 显示数字常量 定位到函数的定义位置 Matlab 自带的集成开发环境(IDE&am…...
Stable Diffusion初步见解(二)
Stable Diffusion 是一种先进的深度学习模型,用于生成高质量的图像和艺术作品。它基于扩散模型(Diffusion Models),并结合了潜在扩散模型(Latent Diffusion Models)以及条件生成技术(如文本到图…...
前端框架 react 性能优化
目录 一、不使用任何性能优化API进行优化 二、通过性能优化API优化 1、React.memo 2、useCallback 3、useMemo 4、PureComponent 三、总结 总览:react的优化核心思想就是让react跳过重新渲染那个些没有改变的Component,而只重新渲染发生变化的C…...
RK3568平台开发系列讲解(Input子系统篇)输入子系统介绍
🚀返回专栏总目录 文章目录 一、什么是输入子系统?二、输入设备和节点的关系沉淀、分享、成长,让自己和他人都能有所收获!😄 一、什么是输入子系统? 在 Linux 中,input 子系统是专门为处理输入类设备而设计的一个子系统或框架。它提供 了一套通用的接口和机制,用于驱…...
准备阶段 Profiler性能分析工具的使用(一)
Unity 性能分析器 (Unity Profiler) 性能分析器记录应用程序性能的多个方面并显示相关信息。使用此信息可以做出有关应用程序中可能需要优化的事项的明智决策,并确认所做的优化是否产生预期结果。 默认情况下,性能分析器记录并保留游戏的最后 300 帧&a…...
go-rod vs Selenium:自动化测试工具的比较与选择
自动化测试是软件开发过程中的关键环节,它能够帮助我们发现缺陷、验证功能并提高软件质量。随着Web技术的快速发展,市场上出现了多种自动化测试工具,其中Selenium和go-rod是两个备受关注的选择。本文将从多个维度对这两个工具进行比较&#x…...
探索免费的Figma中文版:开启高效设计之旅
在当今数字化设计的浪潮中,Figma以其强大的云端协作功能和出色的设计能力,成为了众多设计师的心头好。而对于国内的设计师来说,能够免费使用Figma中文版更是一大福音,下面就来一起探索一下吧。 一、Figma中文版的获取途径 虽然F…...
功能齐全,支持协作 | Docker部署一款支持多人共享的私密浏览器『n.eko』
功能齐全,支持协作 | Docker部署一款支持多人共享的私密浏览器『n.eko』 哈喽小伙伴们好,我是Stark-C~ 玩NAS的朋友基本都会在本地部署一款浏览器用来远程访问内网的网络设备,或者偶尔拿来浏览一些私密网站都是很方便的。 今天为大家分享的…...
部署实战(二)--修改jar中的文件并重新打包成jar文件
一.jar文件 JAR 文件就是 Java Archive ( Java 档案文件),它是 Java 的一种文档格式JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中,多出了一个META-INF/MANIFEST.MF 文件META-INF/MANIFEST.MF 文件在生成 JAR 文件的时候…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
GitHub 趋势日报 (2025年06月06日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...
