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

相关文章

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

golang内存对齐的项目实践

《golang内存对齐的项目实践》本文主要介绍了golang内存对齐的项目实践,内存对齐不仅有助于提高内存访问效率,还确保了与硬件接口的兼容性,是Go语言编程中不可忽视的重要优化手段,下面就来介绍一下... 目录一、结构体中的字段顺序与内存对齐二、内存对齐的原理与规则三、调整结构体字段顺序优化内存对齐四、内

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

SpringBoot中使用 ThreadLocal 进行多线程上下文管理及注意事项小结

《SpringBoot中使用ThreadLocal进行多线程上下文管理及注意事项小结》本文详细介绍了ThreadLocal的原理、使用场景和示例代码,并在SpringBoot中使用ThreadLo... 目录前言技术积累1.什么是 ThreadLocal2. ThreadLocal 的原理2.1 线程隔离2

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

c++中std::placeholders的使用方法

《c++中std::placeholders的使用方法》std::placeholders是C++标准库中的一个工具,用于在函数对象绑定时创建占位符,本文就来详细的介绍一下,具有一定的参考价值,感兴... 目录1. 基本概念2. 使用场景3. 示例示例 1:部分参数绑定示例 2:参数重排序4. 注意事项5.

使用C++将处理后的信号保存为PNG和TIFF格式

《使用C++将处理后的信号保存为PNG和TIFF格式》在信号处理领域,我们常常需要将处理结果以图像的形式保存下来,方便后续分析和展示,C++提供了多种库来处理图像数据,本文将介绍如何使用stb_ima... 目录1. PNG格式保存使用stb_imagephp_write库1.1 安装和包含库1.2 代码解