首页 > 系统相关 >C++中低级内存操作

C++中低级内存操作

时间:2023-10-30 13:02:10浏览次数:32  
标签:收集 内存 C++ 垃圾 字符串 中低级 指针


C++中低级内存操作

C++相较于C有一个巨大的优势,那就是你不需要过多地担心内存管理。如果你使用面向对象的编程方式,你只需要确保每个独立的类都能妥善地管理自己的内存。通过构造和析构,编译器会帮助你管理内存,告诉你什么时候需要进行内存操作。将内存管理隐藏在类中显著提高了可用性,这一点在标准库类中得到了很好的体现。

然而,在某些应用程序或遗留代码中,你可能会遇到需要更低级别地操作内存的情况。无论是出于遗留代码、效率、调试还是好奇心,了解如何操作原始字节总是有帮助的。

指针

C++编译器会使用指针的声明类型来允许你进行指针算术。如果你声明了一个指向整数的指针并将其增加1,那么这个指针在内存中前进的距离是整数的大小,而不是一个单一的字节。这种操作对数组最有用,因为数组包含的数据是类型一致且在内存中连续的。

例如,假设你在自由存储区声明了一个整数数组:

int* myArray { new int[8] };

你可能已经熟悉了以下用于设置索引为2的值的语法:

myArray[2] = 33;

通过使用指针算术,你也可以使用以下等效的语法,该语法获取到myArray“向前两个整数”处的内存的指针,然后解引用它以设置该值:

*(myArray + 2) = 33;

作为访问单个元素的另一种语法,指针算术看起来可能不太吸引人。但其真正的力量在于,像myArray+2这样的表达式仍然是一个指向整数的指针,因此可以表示一个更小的整数数组。

让我们通过一个使用宽字符串的例子来看看。宽字符串支持所谓的Unicode字符,以表示例如日语字符串。wchar_t类型是一个可以容纳这种Unicode字符的字符类型,并且通常比char要大;也就是说,它不仅仅是一个字节。要告诉编译器一个字符串字面量是一个宽字符串字面量,可以在其前面加上一个L

例如,假设你有以下宽字符串:

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

进一步假设你有一个函数,该函数接受一个宽字符串并返回一个包含输入字符串大写版本的新字符串:

wchar_t* toCaps(const wchar_t* text);

请记住,C风格的字符串是以零结尾的,即它们的最后一个元素包含\0。因此,没有必要在函数中添加一个大小参数来指定输入字符串的长度。该函数只需不断地迭代字符串的各个字符,直到遇到\0字符为止。

通过将myString传递给toCaps()函数,你可以将其全部转换为大写。然而,如果你只想部分地大写化myString,你可以使用指针算术来仅引用字符串的后一部分。下面的代码通过仅向指针加7来调用toCaps()函数,以处理宽字符串中的“World”部分,尽管wchar_t通常超过1个字节:

toCaps(myString + 7);

另一个有用的指针算术应用涉及到减法。从同一类型的另一个指针中减去一个指针会给你两个指针之间指向类型的元素数量,而不是它们之间的绝对字节数。

自定义内存管理

在你将遇到的99%(有人可能会说100%)的情况中,C++中的内置内存分配功能是足够的。在幕后,newdelete完成了以适当大小的块分配内存、维护可用内存区域列表以及在删除时将内存块释放回该列表的所有工作。但是,当资源约束非常紧张,或者在非常特殊的条件下,例如管理共享内存,实施自定义内存管理可能是一个可行的选项。不用担心,这并不像听起来那么可怕。

基本上,自己管理内存意味着类会分配一大块内存,并根据需要将该内存分配出去。这种方法有什么好处呢?管理自己的内存可能会减少开销。当你使用new来分配内存时,程序还需要预留一小部分空间以记录分配了多少内存。这样,当你调用delete时,可以释放适当数量的内存。对于大多数对象,开销比分配的内存小得多,因此影响甚微。然而,对于小对象或有大量对象的程序,开销可能会产生影响。当你自己管理内存时,你可能会提前知道每个对象的大小,因此你可能能够避免每个对象的开销。对于大量的小对象来说,差异可能是巨大的。

执行自定义内存管理需要重载newdelete操作符。

垃圾收集

在支持垃圾收集的环境中,程序员很少(如果有的话)明确释放与对象关联的内存。相反,不再有任何引用的对象将在某个时候被运行时库自动清理。垃圾收集没有像在C#和Java中那样内置到C++语言中。在现代C++中,你使用智能指针来管理内存,而在遗留代码中,你将看到通过newdelete在对象级别进行内存管理。

诸如shared_ptr这样的智能指针提供了与垃圾收集内存非常相似的东西;也就是说,当某一资源的最后一个shared_ptr实例被销毁时,该资源也会在那个时刻被销毁。在C++中实现真正的垃圾收集是可能但不容易的,但释放自己从释放内存的任务中可能会引入新的问题。

标记-清除垃圾收集

一种垃圾收集的方法被称为“标记-清除”(Mark and Sweep)。在这种方法下,垃圾收集器会周期性地检查程序中的每一个指针,并标注哪些内存仍然在使用中。在周期结束时,任何没有被标记的内存被认为是不再使用的,并会被释放。在C++中实现这样的算法并不简单,如果做错了,可能比使用delete更容易出错!

尽管在C++中已经有了安全且简单的垃圾收集机制的尝试,但即使有了完美的C++垃圾收集实现,也不一定适用于所有应用程序。垃圾收集的缺点包括:

  • 当垃圾收集器正在运行时,程序可能变得无响应。
  • 在使用垃圾收集器的情况下,你会遇到所谓的“非确定性析构函数”。因为一个对象在被垃圾收集之前不会被销毁,所以析构函数在对象离开其作用域时不会立即执行。这意味着,由析构函数完成的资源清理(例如关闭文件、释放锁等)直到未来某个不确定的时间才会被执行。

编写垃圾收集机制是非常困难的。你很可能会做错,而且很可能会很慢。因此,如果你确实想在你的应用程序中使用垃圾收集内存,我强烈建议你研究现有的专门的垃圾收集库,并重用它们。

对象池

垃圾收集就像是为野餐购买盘子,并将用过的盘子留在院子里,以便某人在某个时候将它们捡起来并扔掉。肯定有更环保的内存管理方法。对象池就是回收的等价物。你购买了一定数量的盘子,使用过一个盘子后,你将其清洗,以便以后可以再次使用。

对象池是理想的解决方案,适用于你需要在一段时间内多次使用相同类型的多个对象,并且每次创建都会产生开销的情况。


标签:收集,内存,C++,垃圾,字符串,中低级,指针
From: https://blog.51cto.com/u_16062556/8087708

相关文章

  • 排序算法:选择排序,分别用c++、java、python实现
    选择排序介绍选择排序(SelectionSort)是一种简单的比较排序算法,它的工作原理如下:分区:将待排序的数组分成两个部分,一个部分是已排序的子数组,另一个部分是未排序的子数组。初始时,已排序的子数组为空,而未排序的子数组包含整个数组。选择最小值:从未排序的子数组中找到最小(或最大,根据......
  • 算法题:分别用c++/python/java实现回文数
    回文数是一个数字,从左到右和从右到左读都是相同的数字序列。换句话说,回文数在数值上是对称的。一些常见的回文数示例包括:单个数字:例如1、2、3等,它们本身就是回文数,因为它们只有一个数字。两位数:例如11、22、33等,它们也是回文数,因为它们的左右两个数字相同。多位数:例如121、1331、12......
  • 用c++写一个高精度计算的除法运算
    高精度除以低精度以下这段代码的主要作用是将一个大整数(以字符数组形式表示)除以一个整数,并输出结果。具体来说,代码将大整数a1(“1256”)除以整数b(3),并输出商。#include<iostream>#include<cstdio>#include<cstring>usingnamespacestd;intmain(){chara1[100]="1256";......
  • c++ .h头文件和.cpp源文件关系(转)
    https://www.cnblogs.com/fengzhengfly/p/8884581.htmlhttps://blog.csdn.net/qq_35452533/article/details/77282326头文件是声明,源文件是定义在cpp里包含.h,就会包含该h文件的cpp定义,所以需要在cpp里使用别的.h文件中的函数只能包含该.h,或者已包含的.h中已经包含的改.h,也就......
  • 如何避免JavaScript中的内存泄漏?
    前言过去,我们浏览静态网站时无须过多关注内存管理,因为加载新页面时,之前的页面信息会从内存中删除。然而,随着单页Web应用(SPA)的兴起,应用程序消耗的内存越来越多,这不仅会降低浏览器性能,甚至会导致浏览器卡死。因此,在编码实践中,开发人员需要更加关注与内存相关的内容。因此,小编今天将......
  • c++同样结构体名冲突报错
    两个不同的头文件中有同名的结构体在编译项目文件的时候出现报错,结构体多次定义,其中一个头文件a的结构体是typedefstruct{doublex,y;}Point;另外一个头文件b是typedefstructPoint{ int16cardinality; uint8num_coords; /*Opendimensioncoordinatesa......
  • 如何避免JavaScript中的内存泄漏?
    前言过去,我们浏览静态网站时无须过多关注内存管理,因为加载新页面时,之前的页面信息会从内存中删除。然而,随着单页Web应用(SPA)的兴起,应用程序消耗的内存越来越多,这不仅会降低浏览器性能,甚至会导致浏览器卡死。因此,在编码实践中,开发人员需要更加关注与内存相关的内容。因此,小编今天......
  • Linux 虚拟内存参数配置
    一、问题出发点Jun110:30:21audit1kernel:swapper:pageallocationfailure.order:1,mode:0x20Jun110:30:21audit1kernel:Pid:0,comm:swapperTainted:G---------------T2.6.32-431.20.3.el6.x86_64#1Jun110:30:21audit1kernel:CallTrace:Jun11......
  • DM数据库架构原理之内存结构
    2.DM数据库体系架构谈DM数据库内存结构之前,我们必须先了解下DM数据库的体系架构,本人认为要想学好一门数据库,必须要深入了解它的体系架构,下面这幅图是本人对DM体系架构理解画出来的。工作原理:下面我们从一个用户请求开始讲,DM的完整的工作机制是咋样的,首先当客户端的发出一条select......
  • .NET中的数组在内存中如何布局?
    总的来说,.NET的值类型和引用类型都映射一段连续的内存片段。不过对于值类型对象来说,这段内存只需要存储其字段成员,而对应引用类型对象,还需要存储额外的内容。就内存布局来说,引用类型有两个独特的存在,一个是字符串,另一个就是数组。我在《你知道.NET的字符串在内存中是如何存储的吗?......