习惯于垃圾回收语言的人通常会害怕C ++的内存管理。有工具,如auto_ptr
和shared_ptr
将处理许多适合你的存储管理任务。许多C ++库早于这些工具,并拥有自己的方式来处理内存管理任务。
您花多少时间执行内存管理任务?
我怀疑它在很大程度上取决于您使用的库集,因此请说出您的答案适用于哪些库,以及它们的优劣之处。
习惯于垃圾回收语言的人通常会害怕C ++的内存管理。有工具,如auto_ptr
和shared_ptr
将处理许多适合你的存储管理任务。许多C ++库早于这些工具,并拥有自己的方式来处理内存管理任务。
您花多少时间执行内存管理任务?
我怀疑它在很大程度上取决于您使用的库集,因此请说出您的答案适用于哪些库,以及它们的优劣之处。
Answers:
现代C ++使您不必担心内存管理,除非您必须这样做,即直到您需要手动组织内存(主要是出于优化目的),或者如果上下文迫使您执行内存管理(认为是大型约束硬件)。我编写整个游戏时都没有操纵原始内存,只是担心使用容器是完成任务的正确工具,就像使用任何语言一样。
因此,它取决于项目,但是在大多数情况下,不是必须要处理的内存管理,而是对象生存期。这是使用智能指针解决的,它是RAII产生的惯用C ++工具之一。
一旦您了解了RAII,内存管理将不再是问题。
然后,当您需要访问原始内存时,将使用非常具体,本地化和可识别的代码(例如在池对象实现中)而不是“无处不在”来进行处理。
在这种代码之外,您不需要操纵内存,而只需对象的生命周期。
“难”的部分是要了解RAII。
delete
除非您有一个糟糕的实现,否则RAII不会比手动占用更多的堆栈空间。
return
)
内存管理用于吓children孩子,但这只是程序员必须照顾的一种资源。考虑文件句柄,网络连接以及从OS获得的其他资源。
支持垃圾回收的语言通常不仅会忽略这些资源的存在,而且还会因不提供析构函数而使正确处理这些资源变得更加困难。
简而言之,我建议不要花太多的C ++开发人员时间来担心内存管理。正如klaim的回答所表明的,一旦您掌握了RAII,剩下的就只是反射。
finalize
构造。但是,您不知道何时调用它。是在套接字或WebResponse对象用尽之前?您会发现很多文章告诉您不应该依靠finalize
-有充分的理由。
几乎没有。即使是像COM这样的旧技术,也可以为Standard指针编写自定义删除器,从而在很短的时间内将它们转换。例如,std::unique_ptr
可以将其转换为使用五行自定义删除器来唯一保存COM引用。即使您必须手动编写自己的资源处理程序,诸如SRP和复制和交换之类的知识的普及也使得编写资源管理类以永久使用的相对容易。
事实是,共享,唯一和非所有权都随C ++ 11编译器一起提供,您只需要编写小型适配器即可使其即使在旧代码下也能正常工作。
当我是C ++程序员(很久以前)时,我花了很长时间担心尝试修复难以重现的错误时的内存管理错误。
使用调制解调器C ++,内存管理已不再是一个大问题,但是您可以信任大型团队中的每个人都可以做到这一点。什么是成本/时间:
因此,不仅是花费时间在“ 做 ”上,这在大型项目上更是一个问题。
客户需要多少时间?一旦掌握了,就很少了。当容器管理生命周期和引用时,这确实非常容易。imo,它比手动引用计数简单得多,并且如果您考虑用作文档的容器,它实际上是透明的,编译器可以方便地阻止您在设计良好的类型安全系统中执行无效的所有权转让。
我(作为客户)花费的大部分时间都花在包含来自其他api的类型上,因此它们在您的程序上下文中运行良好。例如:这是我的ThirdPartyFont容器,它支持这些功能,并实现破坏这种方式,并引用计数这种方式,并复制这种方式,和...。这些构造中的许多构造都需要放置到位,并且通常将它们放置在逻辑上。是否要将其包含在时间中取决于您的定义(无论如何,与这些API交互时,实现都必须存在)?
之后,您将需要考虑内存和所有权。在较低级别的系统中,这是很好且必要的,但是要实现如何移动内容可能会花费一些时间和脚手架。我不觉得这很痛苦,因为这是较低级别系统的要求。所有权,控制权和责任感是显而易见的。
因此我们可以将其转向使用不透明类型的基于C的api:我们的容器使我们能够抽象化管理寿命和复制这些不透明类型的所有小实现细节,这最终使资源管理非常简单,并节省了时间,缺陷,并减少了实现。
使用它们真的非常简单-问题(来自GC)是您现在必须考虑资源的生命周期。如果弄错了,可能要花很多时间才能解决。相比之下,学习和集成明确的生命周期管理是很复杂的(并非对所有人而言),这是真正的障碍。一旦您可以轻松地控制生命周期并使用好的解决方案,那么管理资源生命周期就非常容易。这不是我每天的重要工作(除非遇到了困难的错误)。
如果您不使用容器(自动/共享指针),那么您只是在痛苦。
我已经实现了自己的库。我花了一些时间来实现这些东西,但是大多数人都在重用(通常是个好主意)。
您的意思是像手动释放内存,关闭文件之类的事情?如果是这样,我想说的是最低限度的语言,通常少于我使用过的大多数其他语言,尤其是当我们将其概括为不仅是“内存管理”而是“资源管理”时。从这种意义上讲,我实际上认为C ++比Java或C#需要更少的手动资源管理。
这主要是由于析构函数自动破坏资源(内存或其他)。通常,只有在我要实现vlow级别的数据结构(大多数人不需要做的事情)或使用仅花费一点时间的C API时,才需要在C ++中手动释放/销毁资源。将需要手动释放/销毁/关闭的C资源包装到符合RAII的C ++包装器中。
当然,如果用户要求在图像编辑软件中关闭图像,则必须从收藏夹或其他内容中删除图像。但是希望不要将这种情况视为在这种情况下很重要的“内存”或“资源”管理,因为如果您想在那时释放与该图像关联的内存,那么几乎所有语言都需要这样做。但是同样,您要做的就是从集合中删除图像,然后由图像析构函数负责其余的工作。
同时,如果我将其与Java或C#进行比较,通常会发现人们不得不在那里手动关闭文件,手动断开套接字,将对象引用设置为null以允许对其进行垃圾回收等。还有更多的手动内存和如果您问我,用这些语言进行资源管理。在C ++中,您通常甚至不需要unlock
手动使用互斥锁,因为互斥锁超出范围时,互斥锁会自动为您执行此操作。例如,您永远不必在C ++中执行以下操作:
System.IO.StreamReader file = new System.IO.StreamReader(path);
try
{
file.ReadBlock(buffer, index, buffer.Length);
}
catch (System.IO.IOException e)
{
...
}
finally
{
if (file != null)
file.Close();
}
无需做任何事情,例如在C ++中手动关闭文件。无论结果是超出范围还是正常或异常执行路径,它们最终都会在超出范围时立即自动关闭自己。类似与内存相关的资源,例如std::vector
。像file.Close()
上面这样的代码通常会被皱着眉头,因为,特别是在一个finally
块的上下文中,这表明当围绕C ++的整个思路要自动化时,本地资源需要手动释放。
在手动内存管理方面,我想说C要求最大,Java / C#要求中等,而C ++要求最小。有很多理由不愿使用C ++,因为它是一种很难掌握的语言,但是内存管理不应该是其中之一。相反,我实际上认为这是这方面最简单的语言之一。
当然,C ++确实允许您开始手动分配内存并调用operator delete/delete[]
以手动释放内存。它还允许您使用C函数,例如malloc
和free
。但这是一种古老的编码方法,我认为这种方法早在人们赞扬之前就已经过时了,因为Stroustrup在他甚至从很早就创造出该术语之前就提倡RAII。因此,我什至不认为说“现代C ++”实现资源管理自动化是不公平的,因为这一直都是我们的目的。否则,您几乎无法获得异常安全性。只是在90年代初期,许多被误导的开发人员试图将C ++像使用对象的C一样使用,常常完全忽略了异常处理,并且从来不应该以这种方式使用它。如果您使用C ++实际上总是打算使用它的方式,那么内存管理是完全自动化的,通常不需要您手动处理(或应该处理)很多东西。