C++学习笔记----6、内存管理(三)---- 底层内存操作

2024-09-07 02:52

本文主要是介绍C++学习笔记----6、内存管理(三)---- 底层内存操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        C++相对于C的一个非常大的优势就是你不必太担心内存。如果你的代用到了对象,只需要确信每个类可以好好管理自己的内存。通过构造与析构函数,编译器通过告诉你什么时候去做来帮助你管理内存。在类内隐藏了对内存的管理在使用上带来了很大的不同,就像标准库类展示的那样。然而,对于一些应用或者遗留代码,你可能也会碰到需要在底层处理内存。不管是遗留代码、效率、排错或者是好奇,懂得一些对于原始字节的操作技巧还是很有帮助的。

1、指针的算术运算

        C++编译器使用指针的声明类型来允许你执行指针的算术运算。如果你声明了一个整数指针,然后对指针进行加1操作,指针就会在内存中以整型的大小向前移动,而不是以一个单独字节的长度进行移动。对于数组来说这种类型的操作非常有用,因为它们在内存中包含了顺序的同类型的数据。例如,假设你声明了一个在栈上的整型数组:

int* myArray { new int[8] };

你应该对于下面的将索引值为2的元素进行赋值的语法很熟悉了吧:

myArray[2] = 33;

        对于指针算术运算,你可以同样地使用下面的语法,它包含了一个myArray数组的“2个整数之前”的内存的指针,然后通过引用取值来对其进行赋值:

*(myArray + 2) = 33;

        作为访问单个元素的另一种语法,指针算术运算并不那么吸引人。它真正的能力在于事实上像myArray+2这样的表达式仍然是一个指向Int的指针,可以代表一个小一点的整型数组。

        让我们看一个宽字符串的例子。宽字符串会在以后讨论,在我们要表达的观点上其细节并不重要。现在,只要知道宽字符串支持Unicode字符就可以了,例如,日文字符串。wchar_t类型是一个能够放置这样的Unicode字符的字符类型,一般来说要比char大;也就是说,其长度不仅是一个字节。告诉编译器一个字符串常量是一个宽字符串常量,以L为前缀。例如,假设你有下面的宽字符串:

const wchar_t* myString { L"Hello, World" };

        假定你还要有一个以一个宽字符串为输入参数的函数,返回一个包含了输入参数的大写版本的新字符串:

wchar_t* toCaps(const wchar_t* text);

        你可以通过将myString传递给这个函数来将其转化为大写。然而,如果你只是要将myString的部分进行大写转化的话,你可以使用指针算术运算来指向字符串的后面的一部分。下面的代码调用了对World部分的宽字符串的toCaps()函数,简单地在指针上进行了加7的操作,即使wchar_t通常是大于1个字节的:

toCaps(myString + 7);

        另一个指针算术运算的用处就是减法。将一个指针从另一个同类型的指针相减会给出两个指针之间指向类型的元素数量,而不是它们之间的绝对字节数。

2、客户化内存管理

        你碰到的99%的情形(有人可能会说100%),在c++中内置的内存分配工具就足够了。在这背后,new与delete干了所有以适合大小的chunk进行内存分发的所有工作,维护了可用内存区域的列表,在删除时释放内存chunk到列表中。

        当资源限制特别严格时,或者在特别特殊的情况下,例如管理共享内存,实现客户化内存管理可能就是一个可行的选项。别担心----并不像听起来那么吓人。本质上讲,自己管理内存意味着类分配一大串内存,然后根据需要进行小块的内存发放。

        这个方法怎么会好一些呢?管理自己的内存可以有效地减少开销。当你使用new来分配内存时,程序也需要留出一小块空间来记录分配了多少内存。用这种方式,当你调用delete时,合适大小的内存可以被释放。对于大部分对象来说,开销要比内存分配小的多得多,不产生本质性的影响。然而,对于小的对象或者拥有大量对象的程序来说,这个开销可能会有影响。

        当你自己管理内存时,你会预先知道每个对象的大小,也就能够避免每个对象的额外开销。对于大量的小对象来说会有非常大的不同。执行客户化的内存管理需要对new与delete操作符进行重载,这个主题我们以后再讨论。

3、垃圾回收

        在支持垃圾回收的环境,程序员如果有的话,也很少去显式地对对象进行释放内存的操作。事实上,不再被引用的对象会被运行时库在适当时进行自动清理。

        垃圾回收并不像C#与Java一样被集成至c++语言中。在现代c++中,你会用智能指针来管理内存,而在遗留代码中,你会看到在对象层次用new与delete进行内存管理。像shared_ptr(我们会在后面进行讨论)这样的智能指针提供了与内存垃圾回收非常类似的功能;也就是说,对于特定的资源,当最后的shared_ptr实例被破坏时,在这个时点上资源也被破坏了。在c++中实现垃圾回收是可能的,但有没有那么容易,但是,在释放内存的任务中自己进行释放可能会带来新的令人头痛的问题。

        垃圾收集的一个方法被叫做标记且清除。用这个方法,垃圾回收器不断地检查程序中的每个单独的指针,标注所引用的内存依然被使用的事实。在每个循环的结束,任何不再被标注的内存就完成了使命,会被释放掉。在c++中实现这样的算法并不麻烦,但是,如果没有弄对,会比使用delete更容易出错!

        尝试以安全且容易的技术进行垃圾回收已经在C++中进行,但是即使是一个完美的垃圾回收在c++中的实现的出现,也不可能做到对所有应用都适用。其中垃圾回收的负面总结如下:

  • 在垃圾收集器还在运行时,程序不响应了。
  • 对于垃圾收集器,你却有不确定的析构函数。因为在垃圾回收之前,对象不会被破坏,当对象离开其作用范围时,析构函数不会立刻执行。这就意味着被析构函数完成的清理资源(比如关闭文件,释放锁资源等等)会在未来的某个不确定的时间执行。

        写一个垃圾回收装置非常难。不可避免地会出错,还容易出错,更可能的是会变慢,如果你真的想在应用中使用垃圾回收,推荐你调研可重用的既存的专业的垃圾回收库。

4、对象池

        垃圾收集就像为野营购买盘子,把用过的盘子丢在露营地,在某个时间点有人把它们捡起来扔掉。当然了,内存管理也有一个更生态化的方法。

        对象池就是相应的循环池。你买了一些盘子,在用过一个以后,洗好之后留作后用。对象池对于你需要使用同类型的对象多次的情况的理想选择,生成每一个对象都会有开销产生。

        对于使用对象池进行性能提升的细节我们以后再讨论。

这篇关于C++学习笔记----6、内存管理(三)---- 底层内存操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1143850

相关文章

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Linux创建服务使用systemctl管理详解

《Linux创建服务使用systemctl管理详解》文章指导在Linux中创建systemd服务,设置文件权限为所有者读写、其他只读,重新加载配置,启动服务并检查状态,确保服务正常运行,关键步骤包括权... 目录创建服务 /usr/lib/systemd/system/设置服务文件权限:所有者读写js,其他

使用Java填充Word模板的操作指南

《使用Java填充Word模板的操作指南》本文介绍了Java填充Word模板的实现方法,包括文本、列表和复选框的填充,首先通过Word域功能设置模板变量,然后使用poi-tl、aspose-words... 目录前言一、设置word模板普通字段列表字段复选框二、代码1. 引入POM2. 模板放入项目3.代码

利用Python操作Word文档页码的实际应用

《利用Python操作Word文档页码的实际应用》在撰写长篇文档时,经常需要将文档分成多个节,每个节都需要单独的页码,下面:本文主要介绍利用Python操作Word文档页码的相关资料,文中通过代码... 目录需求:文档详情:要求:该程序的功能是:总结需求:一次性处理24个文档的页码。文档详情:1、每个

Python内存管理机制之垃圾回收与引用计数操作全过程

《Python内存管理机制之垃圾回收与引用计数操作全过程》SQLAlchemy是Python中最流行的ORM(对象关系映射)框架之一,它提供了高效且灵活的数据库操作方式,本文将介绍如何使用SQLAlc... 目录安装核心概念连接数据库定义数据模型创建数据库表基本CRUD操作创建数据读取数据更新数据删除数据查

Go语言中json操作的实现

《Go语言中json操作的实现》本文主要介绍了Go语言中的json操作的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录 一、jsOChina编程N 与 Go 类型对应关系️ 二、基本操作:编码与解码 三、结构体标签(Struc

在Node.js中使用.env文件管理环境变量的全过程

《在Node.js中使用.env文件管理环境变量的全过程》Node.js应用程序通常依赖于环境变量来管理敏感信息或配置设置,.env文件已经成为一种流行的本地管理这些变量的方法,本文将探讨.env文件... 目录引言为什么使php用 .env 文件 ?如何在 Node.js 中使用 .env 文件最佳实践引

C++读写word文档(.docx)DuckX库的使用详解

《C++读写word文档(.docx)DuckX库的使用详解》DuckX是C++库,用于创建/编辑.docx文件,支持读取文档、添加段落/片段、编辑表格,解决中文乱码需更改编码方案,进阶功能含文本替换... 目录一、基本用法1. 读取文档3. 添加段落4. 添加片段3. 编辑表格二、进阶用法1. 文本替换2