cppyy 模板类实例化没有虚拟析构函数
Posted
技术标签:
【中文标题】cppyy 模板类实例化没有虚拟析构函数【英文标题】:cppyy template class instantiation has no virtual destructor 【发布时间】:2021-09-18 05:06:11 【问题描述】:我在 Python 代码中设置了以下子类:
class NodeRewriter(SyntaxRewriter[SyntaxNode]):
def visit(self, node: SyntaxNode):
print(node)
以下是相关对象:
RuntimeWarning: class "slang::SyntaxRewriter<slang::SyntaxNode>" has no virtual destructor
class NodeRewriter(SyntaxRewriter[SyntaxNode]):
>>> print(SyntaxRewriter)
<cppyy.Template 'slang::SyntaxRewriter' object at 0x7fb76c0c15b0>
>>> print(SyntaxNode)
<class cppyy.gbl.slang.SyntaxNode at 0x55e4302f07c0>
>>> print(SyntaxRewriter[SyntaxNode])
<class cppyy.gbl.slang.SyntaxRewriter<slang::SyntaxNode> at 0x55e4308df4b0>
>>>
我所做的只是创建类,我还没有从中创建任何对象。当我导入包含类和其余 cppyy 代码的文件时收到警告。
我需要自己添加一个__destruct__()
方法吗?似乎警告不是指我的子类,而是指它继承自的模板实例化。
【问题讨论】:
【参考方案1】:没看过SyntaxRewriter
的出处...
如果 C++ 中的类没有虚拟析构函数,它通常不适合作为基类,因为如果在基类指针上调用 delete
,则只会调用基类的析构函数,而不是指针底层的实际派生类。有时这没关系,有时会出现资源泄漏,有时会出现虚假崩溃。
如果全部在 C++ 中完成,那么当你这样做时它会更加明确,只要你从不 delete
这样的基类指针就可以了。代码是明确的,你可以小心不要这样做。但是在交叉派生中,在 C++ 基类和 Python 派生类之间存在一个存根(stub)可能并不明显,既可以调解调用也可以管理内存。因此,该存根和实际删除都被隐藏了。此外,交叉派生最常见的用例是将派生的 Python 类的实例发送到 C++ 领域,在那里它们可能会被管理。然后,如果 C++ 在某个时候清理了对象,而没有 C++ 基础的虚拟析构函数,则不会调用存根的析构函数,并且 Python 部分会泄漏(它的引用计数太多而永远不会被清除)。如果 Python 管理对象,这只是一个警告 b/c,即使没有虚拟析构函数也不会泄漏。
实现__destruct__
不会产生任何影响(在任何情况下都不应该这样做;如果需要清理资源,请改用__del__
),因为如果有问题的话,是由以前编译的 C++ 引起的并且事后无法更改。
(请注意,可以抑制 Python 警告,请参阅https://docs.python.org/3/library/warnings.html。)
【讨论】:
好的,有道理,谢谢。在使用库多一点之后,我发现模板的类型应该是派生类本身。如果我错了,请纠正我,但我认为这是不可行的,因为 Python 在创建类之前无法引用它。基本上是这样的:class NodeRewriter(SyntaxRewriter[NodeRewriter]):
。我可能最终会在 C++ 中创建派生类,然后在 Python 端对其进行子类化以跳过模板。
是的,那个特定的语法永远不会是有效的 Python,但它在逻辑上并非不可能。重点是 C++ 存根将在模板中使用,模板是 Python 类的基类,因此存在于 Python 类本身之前。 IE。可以通过创建一些不提及类名作为标识符的特殊语法来实现(存根名称在 Python 端是未知的,以免污染层次结构,因此不能直接使用)。
好的,很高兴知道。我会想出另一种解决方案,要么在 C++ 端创建派生类,要么完全绕过它并在我自己的 Python 类中实现功能。以上是关于cppyy 模板类实例化没有虚拟析构函数的主要内容,如果未能解决你的问题,请参考以下文章