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 模板类实例化没有虚拟析构函数的主要内容,如果未能解决你的问题,请参考以下文章

c++中,析构函数和delete各有啥作用啊

多态和抽象

27)构造和析构函数

2016/3/21 面向对象: ①定义类 ②实例化对象 ③$this关键字 ④构造函数 ⑤析构函数 ⑥封装 ⑦继承

c++之构造函数,析构函数(五千字长文详解!)

构造函数和析构函数?它们有什么作用? 面向对象的特征有哪些?