如我所见,智能指针已在许多实际的C ++项目中广泛使用。
尽管某种智能指针显然有利于支持RAII和所有权转移,但也存在一种趋势,默认情况下使用共享指针作为“垃圾收集”方式,因此程序员不必考虑太多分配。
为什么共享指针比集成Boehm GC这样的适当垃圾收集器更受欢迎?(或者您是否完全同意它们比实际的GC更受欢迎?)
我知道常规GC相对于引用计数有两个优点:
- 常规的GC算法在参考循环方面没有问题。
- 参考计数通常比适当的GC 慢。
使用引用计数智能指针的原因是什么?
如我所见,智能指针已在许多实际的C ++项目中广泛使用。
尽管某种智能指针显然有利于支持RAII和所有权转移,但也存在一种趋势,默认情况下使用共享指针作为“垃圾收集”方式,因此程序员不必考虑太多分配。
为什么共享指针比集成Boehm GC这样的适当垃圾收集器更受欢迎?(或者您是否完全同意它们比实际的GC更受欢迎?)
我知道常规GC相对于引用计数有两个优点:
使用引用计数智能指针的原因是什么?
Answers:
与垃圾收集相比,引用计数的一些优点:
低开销。垃圾收集器可能会很麻烦(例如,使程序在垃圾收集周期进行过程中在不可预知的时间冻结),并且会占用大量内存(例如,在垃圾收集最终开始之前,您的进程的内存占用不必要地增加了很多兆字节)
更可预测的行为。通过引用计数,可以确保在上一次引用消失后立即释放对象。另一方面,通过垃圾回收,当系统处理完该对象后,您的对象将“有时”被释放。对于RAM,在台式机或负载较小的服务器上,这通常不是大问题,但对于其他资源(例如,文件句柄),您通常需要尽快将其关闭,以避免以后发生潜在冲突。
更简单 引用计数可以在几分钟内得到解释,在一两个小时内即可实现。垃圾收集器,尤其是性能良好的垃圾收集器,非常复杂,没有多少人了解它们。
标准。C ++在STL中包括引用计数(通过shared_ptr)和朋友,这意味着大多数C ++程序员都熟悉它,并且大多数C ++代码都可以使用它。但是,没有任何标准的C ++垃圾收集器,这意味着您必须选择一个垃圾收集器,并希望它适合您的用例-否则,解决问题是您的问题,而不是语言的问题。
至于引用计数的缺点-不检测周期是一个问题,但是在过去十年中我从未亲自遇到过使用引用计数的问题。大多数数据结构自然是非循环的,如果确实遇到需要循环引用的情况(例如,树节点中的父指针),则可以仅使用weak_ptr或原始C指针作为“向后方向”。只要您在设计数据结构时就意识到潜在的问题,这不是问题。
至于性能,我从未对引用计数的性能有任何疑问。我在垃圾收集的性能方面遇到了问题,特别是GC可能导致的随机冻结,唯一的解决方案(“不分配对象”)也可能改为“不使用GC” 。
make_shared
在返回时,C ++会自然地持续。延迟仍然是实时应用程序中更大的问题,但是吞吐量通常更为重要,这就是为什么跟踪GC如此广泛的原因。我不会很快就对他们说不好。
为了从GC中获得良好的性能,GC必须能够移动内存中的对象。在像C ++这样的语言中,您可以直接与内存位置进行交互,这几乎是不可能的。(Microsoft C ++ / CLR不算在内,因为它为GC管理的指针引入了新语法,因此实际上是另一种语言。)
Boehm GC虽然很漂亮,但实际上是两全其美的情况:您需要一个比好的GC慢的malloc(),因此您将失去确定性的分配/重新分配行为,而不会相应地提高世代GC的性能。另外,它必然是保守的,因此它不一定会收集所有垃圾。
良好且调试良好的GC可能很棒。但是在像C ++这样的语言中,收益微乎其微,而成本却往往不值得。
但是,有趣的是,随着C ++ 11变得越来越流行,lambda和捕获语义是否开始导致C ++社区面临导致Lisp社区在第一时间发明GC的相同类型的分配和对象生存期问题。地点。
如我所见,智能指针已在许多实际的C ++项目中广泛使用。
没错,但客观地讲,现在绝大多数代码是用现代语言编写的,具有跟踪垃圾收集器的功能。
尽管某种智能指针显然有利于支持RAII和所有权转移,但也存在一种趋势,默认情况下使用共享指针作为“垃圾收集”方式,因此程序员不必考虑太多分配。
这是一个坏主意,因为您仍然需要担心周期。
为什么共享指针比集成Boehm GC这样的适当垃圾收集器更受欢迎?(或者您是否完全同意它们比实际的GC更受欢迎?)
哇,您的思路有很多错误:
从任何意义上讲,Boehm的GC都不是“适当的” GC。真可怕。它是保守的,因此会泄漏并且在设计上效率低下。请参阅:http://flyingfrogblog.blogspot.co.uk/search/label/boehm
客观上讲,共享指针远没有GC普及,因为绝大多数开发人员现在都在使用GC语言,并且不需要共享指针。只需看看与C ++相比在就业市场中的Java和Javascript。
您似乎在限制使用C ++,因为我认为您认为GC是一个切线问题。并不是(获得良好的GC 的唯一方法是从一开始就为它设计语言和VM),所以您引入了选择偏见。真正想要正确进行垃圾收集的人不会坚持使用C ++。
使用引用计数智能指针的原因是什么?
您只能使用C ++,但希望您具有自动内存管理功能。
在MacOS X和iOS中,对于使用Objective-C或Swift的开发人员来说,引用计数很受欢迎,因为它是自动处理的,并且由于Apple不再支持垃圾回收,因此垃圾回收的使用已大大减少(有人告诉我,使用垃圾回收将在下一个MacOS X版本中中断,并且垃圾回收从未在iOS中实现)。实际上,我非常怀疑是否有很多软件在垃圾回收可用时使用垃圾回收。
摆脱垃圾回收的原因:在C风格的环境中,指针可能会“逃逸”到垃圾回收器无法访问的区域,因此它永远无法可靠地工作。苹果公司坚信并相信引用计数更快。(您可以在此处声明相对速度,但没有人能够说服Apple)。最后,没有人使用垃圾回收。
任何MacOS X或iOS开发人员要学习的第一件事是如何处理参考周期,因此对于真正的开发人员而言这不是问题。
C ++中垃圾回收的最大缺点是,不可能正确地做到这一点:
在C ++中,指针并不存在于自己的围墙社区中,它们与其他数据混合在一起。因此,您无法将指针与恰好具有可以解释为有效指针的位模式的其他数据区分开。
后果:任何C ++垃圾收集器都会泄漏应收集的对象。
在C ++中,您可以执行指针算术以派生指针。这样,如果找不到指向块开头的指针,则并不意味着不能引用该块。
后果:任何C ++垃圾回收器都必须考虑这些调整,将碰巧指向块内任何位置的任何位序列(包括紧随其结尾之后)都视为引用该块的有效指针。
注意:没有C ++垃圾收集器可以使用以下技巧来处理代码:
int* array = new int[7];
array--; //undefined behavior, but people may be tempted anyway...
for(int i = 1; i <= 7; i++) array[i] = i;
是的,这会引发未定义的行为。但是,某些现有代码比它的优点更聪明,并且可能会触发垃圾回收器进行初步的重新分配。
std::unique_ptr
是足够的,因此就运行时性能而言,其原始指针的开销为零。通过std::shared_ptr
在所有地方使用,您还将使所有权语义模糊不清,从而失去了智能指针(除了自动资源管理之外)的主要好处之一-清楚地了解了代码的意图。