C++ shared_ptr 与 Python 对象
C++ shared_ptr vs. Python object
AFAIK,通常不鼓励使用 shared_ptr,因为不小心使用它们会导致潜在的错误(除非您对显着的好处和仔细检查的设计有很好的解释)。
另一方面,Python 对象似乎本质上是 shared_ptrs(ref_count 和垃圾收集)。
我想知道是什么让它们在 Python 中工作得很好,但在 C++ 中有潜在的危险。换句话说,Python 和 C++ 在处理 shared_ptr 方面有什么区别,这使得它们在 C++ 中的使用受到阻碍,但在 Python 中不会导致类似的问题?
我知道,例如Python 自动检测对象之间的循环,从而防止在 C++ 中悬空循环 shared_ptr 可能导致的内存泄漏。
"I know e.g. Python automatically detects cycles"——这就是使它们工作良好的原因,至少就 "potential bugs" 与内存泄漏有关而言是这样。
除此之外,C++ 程序比 Python 程序更常在严格的性能约束下编写(IMO 是不同真实要求的组合,在经验法则中有一些相当虚假的差异,但那是另一个故事)。我使用的相当大比例的 Python 对象并不严格需要引用计数,它们只有一个所有者, unique_ptr
就可以了(或者 [=96= 的数据成员) ] 类型)。在 C++ 中,它被认为(由编写您正在阅读的建议的人)值得利用性能优势和明确简化的设计。在 Python 中,它通常不被认为是一个问题,你支付性能并保持灵活性,以便稍后决定共享它,毕竟不需要任何代码更改(除了采用比原始引用更长寿的额外引用,我的意思是) .
顺便说一下,在任何语言中,共享可变对象都有 "potential bugs" 与之关联,如果您在不查看对象时忘记了哪些对象会更改或不会更改。我不只是指竞争条件:即使在单线程程序中,您也需要知道 C++ 谓词不应该改变任何东西,并且您(通常)不能在迭代容器时改变它。不过,我不认为这是 C++ 和 Python 之间的区别。相反,在某种程度上,您 应该 也对 Python 中的共享对象保持警惕,并且当您增加对对象的引用时,至少要了解您为什么要这样做。
因此,关于您 link 问题中的问题列表:
- cyclic references -- 如前所述,Python 卷起袖子,找到并释放它们。由于与语言的设计和特定用途有关的原因,循环中断垃圾收集在 C++ 中实现起来相当困难,尽管并非不可能。
- creating multiple unrelated
shared_ptr
s to the same object -- 在 Python 中没有模拟是可能的,因为引用计数器不对用户开放以搞乱。
- Constructing an anonymous temporary shared pointer -- 在 Python 中不会出现,在 Python 中没有内存泄漏的风险,因为没有 "gap" 中的对象存在,但如果未被引用,则尚未收集。
- Calling the
get()
function to get the raw pointer and use it after the pointed-to object goes out of scope —— 好吧,如果你写 Python/C,你可能会把它搞砸,但不是纯 Python.
- Passing a reference of or a raw pointer to a shared_ptr should be dangerous too, since it won't increment the internal count -- 在 Python 中没有办法在没有语言处理引用计数的情况下添加引用。
- we passed 'this' to some thread workers instead of 'shared_from_this'——换句话说,忘记在需要时创建一个
shared_ptr
。无法在 Python. 中执行此操作
- most of the predicates you know and love from
<functional>
don't play nicely with shared_ptr
-- Python 引用计数是内置于 运行time 中的(或者我想更准确地说,我应该说:垃圾收集是内置于语言中的设计),没有图书馆无法应对它。
- Using shared_ptr for really small objects (like char short) could be an overhead -- Python 中存在问题,Python 程序员通常不会为此烦恼。如果您需要 "primitive type" 的数组,那么您可以使用
numpy
来减少开销。有时 Python 程序 运行 内存不足,你需要做点什么,这就是生活 ;-)
- Giving out a
shared_ptr< T >
to this inside a class definition is also dangerous. Use enabled_shared_from_this
instead -- 可能不明显,但这又是"don't create multiple unrelated shared_ptr
to the same object"。
- You need to be careful when you use
shared_ptr
in multithread code -- 也可以在 Python 中创建竞争条件,这是 "shared mutable objects are tricksy". 的一部分
这主要是因为在 C++ 中你必须明确地做一些事情来获得引用计数,如果你不要求它,你就不会得到它。这为 Python 无法提供给程序员的错误提供了几个机会,因为它只是为您做的。如果您正确使用 shared_ptr
那么除了存在不与之合作的库之外,none 这些问题也会出现在 C++ 中。出于这些原因而对使用它持谨慎态度的人基本上是在说,他们担心自己会错误地使用它,或者至少比他们会误用某些替代品更害怕。许多 C++ 编程都在相互权衡不同的潜在错误,直到你想出一个你认为自己有能力执行的设计。此外,它具有 "don't pay for what you don't need" 作为设计理念。在这两个因素之间,如果没有很好的解释、显着的好处和经过仔细检查的设计,您就不会做任何事情。 shared_ptr
也不例外 ;-)
AFAIK, the use of shared_ptr is often discouraged because of potential bugs caused by careless usage of them (unless you have a really good explanation for significant benefit and carefully checked design).
我不同意。除非您有充分的理由不这样做,否则通常会倾向于使用这些智能指针。
shared_ptr that makes their usage discouraged in C++ but not causing similar problems in Python?
嗯,我不知道你最喜欢的大型信号处理框架生态系统,但是 GNU Radio 对他们所有的 块 使用 shared_ptrs,它们是GNU Radio 架构的核心元素。事实上,块是 classes,具有 private
构造函数,只能通过 friend
make
函数访问,而 returns 是 shared_ptr
.我们在这方面没有遇到过问题——GNU Radio 有充分的理由采用这样的模型。现在,我们没有一个地方可以让用户尝试使用释放的块对象,没有一个块被泄露。不错!
此外,我们使用 SWIG 和网关 class 来处理一些不能很好地表示为 Python 类型的 C++ 类型。所有这些在 C++ 和 Python 两边都运行良好。事实上,它工作得非常好,我们可以使用 Python classes 作为 C++ 运行时中的块,包装在 shared_ptr.
中
此外,我们从未遇到过性能问题。 GNU Radio 是一个高速率、高度优化、高度多线程的框架。
AFAIK,通常不鼓励使用 shared_ptr,因为不小心使用它们会导致潜在的错误(除非您对显着的好处和仔细检查的设计有很好的解释)。
另一方面,Python 对象似乎本质上是 shared_ptrs(ref_count 和垃圾收集)。
我想知道是什么让它们在 Python 中工作得很好,但在 C++ 中有潜在的危险。换句话说,Python 和 C++ 在处理 shared_ptr 方面有什么区别,这使得它们在 C++ 中的使用受到阻碍,但在 Python 中不会导致类似的问题?
我知道,例如Python 自动检测对象之间的循环,从而防止在 C++ 中悬空循环 shared_ptr 可能导致的内存泄漏。
"I know e.g. Python automatically detects cycles"——这就是使它们工作良好的原因,至少就 "potential bugs" 与内存泄漏有关而言是这样。
除此之外,C++ 程序比 Python 程序更常在严格的性能约束下编写(IMO 是不同真实要求的组合,在经验法则中有一些相当虚假的差异,但那是另一个故事)。我使用的相当大比例的 Python 对象并不严格需要引用计数,它们只有一个所有者, unique_ptr
就可以了(或者 [=96= 的数据成员) ] 类型)。在 C++ 中,它被认为(由编写您正在阅读的建议的人)值得利用性能优势和明确简化的设计。在 Python 中,它通常不被认为是一个问题,你支付性能并保持灵活性,以便稍后决定共享它,毕竟不需要任何代码更改(除了采用比原始引用更长寿的额外引用,我的意思是) .
顺便说一下,在任何语言中,共享可变对象都有 "potential bugs" 与之关联,如果您在不查看对象时忘记了哪些对象会更改或不会更改。我不只是指竞争条件:即使在单线程程序中,您也需要知道 C++ 谓词不应该改变任何东西,并且您(通常)不能在迭代容器时改变它。不过,我不认为这是 C++ 和 Python 之间的区别。相反,在某种程度上,您 应该 也对 Python 中的共享对象保持警惕,并且当您增加对对象的引用时,至少要了解您为什么要这样做。
因此,关于您 link 问题中的问题列表:
- cyclic references -- 如前所述,Python 卷起袖子,找到并释放它们。由于与语言的设计和特定用途有关的原因,循环中断垃圾收集在 C++ 中实现起来相当困难,尽管并非不可能。
- creating multiple unrelated
shared_ptr
s to the same object -- 在 Python 中没有模拟是可能的,因为引用计数器不对用户开放以搞乱。 - Constructing an anonymous temporary shared pointer -- 在 Python 中不会出现,在 Python 中没有内存泄漏的风险,因为没有 "gap" 中的对象存在,但如果未被引用,则尚未收集。
- Calling the
get()
function to get the raw pointer and use it after the pointed-to object goes out of scope —— 好吧,如果你写 Python/C,你可能会把它搞砸,但不是纯 Python. - Passing a reference of or a raw pointer to a shared_ptr should be dangerous too, since it won't increment the internal count -- 在 Python 中没有办法在没有语言处理引用计数的情况下添加引用。
- we passed 'this' to some thread workers instead of 'shared_from_this'——换句话说,忘记在需要时创建一个
shared_ptr
。无法在 Python. 中执行此操作
- most of the predicates you know and love from
<functional>
don't play nicely withshared_ptr
-- Python 引用计数是内置于 运行time 中的(或者我想更准确地说,我应该说:垃圾收集是内置于语言中的设计),没有图书馆无法应对它。 - Using shared_ptr for really small objects (like char short) could be an overhead -- Python 中存在问题,Python 程序员通常不会为此烦恼。如果您需要 "primitive type" 的数组,那么您可以使用
numpy
来减少开销。有时 Python 程序 运行 内存不足,你需要做点什么,这就是生活 ;-) - Giving out a
shared_ptr< T >
to this inside a class definition is also dangerous. Useenabled_shared_from_this
instead -- 可能不明显,但这又是"don't create multiple unrelatedshared_ptr
to the same object"。 - You need to be careful when you use
shared_ptr
in multithread code -- 也可以在 Python 中创建竞争条件,这是 "shared mutable objects are tricksy". 的一部分
这主要是因为在 C++ 中你必须明确地做一些事情来获得引用计数,如果你不要求它,你就不会得到它。这为 Python 无法提供给程序员的错误提供了几个机会,因为它只是为您做的。如果您正确使用 shared_ptr
那么除了存在不与之合作的库之外,none 这些问题也会出现在 C++ 中。出于这些原因而对使用它持谨慎态度的人基本上是在说,他们担心自己会错误地使用它,或者至少比他们会误用某些替代品更害怕。许多 C++ 编程都在相互权衡不同的潜在错误,直到你想出一个你认为自己有能力执行的设计。此外,它具有 "don't pay for what you don't need" 作为设计理念。在这两个因素之间,如果没有很好的解释、显着的好处和经过仔细检查的设计,您就不会做任何事情。 shared_ptr
也不例外 ;-)
AFAIK, the use of shared_ptr is often discouraged because of potential bugs caused by careless usage of them (unless you have a really good explanation for significant benefit and carefully checked design).
我不同意。除非您有充分的理由不这样做,否则通常会倾向于使用这些智能指针。
shared_ptr that makes their usage discouraged in C++ but not causing similar problems in Python?
嗯,我不知道你最喜欢的大型信号处理框架生态系统,但是 GNU Radio 对他们所有的 块 使用 shared_ptrs,它们是GNU Radio 架构的核心元素。事实上,块是 classes,具有 private
构造函数,只能通过 friend
make
函数访问,而 returns 是 shared_ptr
.我们在这方面没有遇到过问题——GNU Radio 有充分的理由采用这样的模型。现在,我们没有一个地方可以让用户尝试使用释放的块对象,没有一个块被泄露。不错!
此外,我们使用 SWIG 和网关 class 来处理一些不能很好地表示为 Python 类型的 C++ 类型。所有这些在 C++ 和 Python 两边都运行良好。事实上,它工作得非常好,我们可以使用 Python classes 作为 C++ 运行时中的块,包装在 shared_ptr.
中此外,我们从未遇到过性能问题。 GNU Radio 是一个高速率、高度优化、高度多线程的框架。