我应该将所有对 new/delete 的调用集中到一个类吗?
Posted
技术标签:
【中文标题】我应该将所有对 new/delete 的调用集中到一个类吗?【英文标题】:Should I centralize all calls to new/delete to a single class? 【发布时间】:2011-09-18 00:16:07 【问题描述】:目前,我的所有对象都管理自己的内存,通常在其构造函数中使用new
进行分配,并在其析构函数中使用delete
。这目前有效,但我拥有的使用任意数量内存的类数量正在增长。 new
本质上是一个“请求”这一事实也让我感到困扰,因为这些对象中没有代码来处理被告知“不”的情况,如果我不需要,我不想依赖异常处理。
在性能方面是否有益于完全 将所有分配内存的调用屏蔽到一个处理 堆上的每个内存分配,可能分配大块 一次并使用placement new来处理参考?
在较小的类中使用内存分配是否是一个足够大的问题,甚至会为此烦恼?
我还能使用 STL 容器并强制它们使用我的堆吗 提供?
提前谢谢你!
【问题讨论】:
“这些对象中没有代码来处理被告知“不”,如果我不需要,我不想依赖异常处理”-你还想什么其他方式处理这个?您要么有支票,要么有例外。内存不是无限的,所以没有其他选择。 @merlyn - 如果我将它集中到一个类中,检查将根据请求在内存管理类中进行。然后我只需要在那里放置所有需要的代码。例如,我可以提示用户释放内存,或者在程序尝试再次分配内存时暂停程序。或者只是在那个时候终止。 您仍然拥有所有这些异常处理选项,并且您可以将代码放在它所属的位置 - 了解系统中正在发生的事情的更高级别的代码(因此可以进行释放然后重试操作,或向用户显示消息)。当然你可以争辩说你的分配器应该有一个“检查是否有足够空间”的方法,所以你可以在抛出异常之前做到这一点...... “例如,我可以提示用户释放内存,或者在程序尝试再次分配内存时停止程序。或者直接终止”——这就是std::set_new_handler
的用途。另请注意,在内存不足的情况下很难向用户显示消息,您必须确保提前分配了所有必要的资源。
@Steve - 我不知道它存在。那是...正是我一直在寻找的,谢谢! (我想为了显示一条消息,在一开始就保留内存以供关键使用会有所帮助)
【参考方案1】:
我还能使用 STL 容器并强制它们使用我提供的堆吗?
STL 容器接受自定义分配器:
http://en.wikipedia.org/wiki/Allocator_(C%2B%2B)#Custom_allocators
这是一个带有示例链接的线程:
Compelling examples of custom C++ allocators?
在性能方面是否有益...?
您只能通过编写应用程序、提出一组可重现的测试场景以及在分析器中运行代码来找出答案。如果您发现内存分配占运行时间的很大一部分,那么您可能会从更好的分配策略中受益。
如果您可以将程序分解为功能级别,并且可以针对每种情况提出现实场景,那么您不必让整个程序都工作来执行此操作。但是请记住,花在优化上的时间是可以花在测试或编码新功能上的时间:) 只做必要的事情......
在较小的类中使用内存分配是一个足够大的问题,甚至会为此烦恼吗?
根据您的程序,您对大分配故障的敏感程度,您在循环中分配的频率等,这是可能的。简介:)
在开发应用时,您仍然可以对分配敏感 - 尽可能创建自动存储(堆栈本地)变量,并仅在必须时动态分配。
【讨论】:
明白!我的分析器仅将其视为某些物理内容的接触对生成中的一个小问题。不过此时,我只想将我的错误检查代码放在某个地方,所以我不必将样板代码放在任何地方。性能提升只是我很好奇的事情 @Clairvoire:您还可以考虑将错误检查代码放在工厂方法/类中。【参考方案2】:我不确定我是否理解您的问题。也就是说,在 C++03 中将 STL 容器与您的自定义堆一起使用将具有挑战性,因为分配器被认为是无状态的。另外,你为什么不想依赖异常处理呢?你知道有一个 no_throw 版本的 new 吗?
编辑: new 的无抛出版本调用如下:new (std::nothrow) Type[size];
。如果分配失败,它会返回一个空指针(0
)而不是抛出std::bad_alloc
。
【讨论】:
我不知道 :) 你如何调用new
的 no_throw 版本?您可以将此添加到您的答案中吗?
我知道,但是使用 no_throw 意味着我必须检查 NULL。本身不是问题,但是该级别的类通常无法从被拒绝的内存中恢复。至于异常处理,只是偏好而已。【参考方案3】:
我还能使用 STL 容器并强制它们使用我提供的堆吗?
是的,查找 STL allocators。
完全屏蔽所有分配内存的调用对处理堆上每个内存分配的单个类(可能一次分配大块并使用placement new处理引用)在性能方面有益吗?
这基本上就是malloc
或new
的良好实现。我不建议您自己做,除非情况非常对性能非常关键并且其他所有内容都已优化。为什么?因为经过深思熟虑的内存管理即使没有错误也很难,更不用说优化工作了。
在较小的类中使用内存分配是一个足够大的问题,甚至会为此烦恼吗?
这取决于您是否正在为具有 16k 内存的咖啡机或游戏设备进行编程,但在普通台式电脑或笔记本电脑上可能不会。另请记住,堆栈非常快(分配和访问),而堆在分配和使用方面稍差(不太确定 TBH),因此在日常情况下您希望更喜欢堆栈。
【讨论】:
...或者它是瓶颈,所以不要先优化其他东西 @Merlyn Morgan-Graham:是的 :) +1;至于目标系统的东西(咖啡机),主机视频游戏系统也受益于谨慎的分配策略,因为开发人员倾向于将每个资源预算榨干,而不是“我们会做我们想做的事情并告诉用户升级” 【参考方案4】:你说你在你的构造函数中调用new
...真的有必要吗?我的意思是,而不是这个......
class A
std::vector<int>* v;
A( int vectorSize )
v = new std::vector<int>( vectorSize, 0 );
~A()
delete v;
;
...这样做总是比较可取的:
class A
std::vector<int> v;
A( int vectorSize ):
v( vectorSize, 0 )
;
这样可以避免使用堆。只有在别无选择时才应该使用堆。
除此之外,就像其他人所说的那样,编写自定义分配器是一项非常复杂的任务,只能在性能关键的场景中完成
【讨论】:
std::vector<int> v;
在堆栈上分配容器本身,但我一直认为容器使用的实际内存是在堆上创建的。
啊,现在我明白你的意思了。由于vectors
需要动态增长,他们别无选择,只能使用堆。虽然如果他们在堆栈上分配足够的初始空间,他们可能会避免它,并且只有在超过该限制时才请求堆空间。无论如何,这是一个非常特定于实现的细节。 Visual Studio 的做法可能与 gcc 不同。
+1;赞成,因为这是一个很好的建议,即使仔细检查不适用。【参考方案5】:
我以前做过,这是我的经验:
-
它很快就会变得非常混乱。特别是因为您现在将内存分配掌握在自己手中,并且您必须处理诸如碎片和重入之类的事情。
但是,由于能够绕过操作系统开销,我发现性能提升超过 20%。
对于您的最后一个问题,我认为有一种方法可以让他们使用自定义分配器,但我以前从未这样做过。我的大部分编码都是用 C 语言完成的。
编辑:
根据您的评论,这里有一个更新。您实际上不必处理构建分配器。您可能只需将所有内存分配都指向您的自定义类即可。然后,您的自定义类将调用 malloc()
或 new
并捕获返回的任何 NULL 或异常。
(虽然将每个new
替换为您自己的malloc()
需要一些工作。)
【讨论】:
以上是关于我应该将所有对 new/delete 的调用集中到一个类吗?的主要内容,如果未能解决你的问题,请参考以下文章