这不是我第一次得到这个RuntimeError: underlying C/C++ object has been deleted.我已经解决了很多次,以随机但直观的方式改变我的代码,但现在我再次面对这一点,只是不明白为什么会发生......我要求的是面对和解决这个错误的通用方法.
我不会在这里发布代码示例,因为我的项目太复杂了,我只是无法弄清楚bug的位置.而且因为我要求通用的解决方案不仅仅是针对这种情况.
为什么可以删除"底层C/C++"对象?
怎么避免呢?
如何测试底层对象是否存在?
cha*_*ley 13
您无法测试底层对象是否存在(例如,是否已被"删除").你可以使用一些"技巧"来帮助这种"善意",但我不推荐它.相反,恕我直言,我猜你的设计缺乏强大的所有权语义.(这可能是必要的,具体取决于问题和域名的复杂程度,但如果可以避免,我建议不要这样做.)
您的问题是所有权语义.尤其是在C++中,与使用内存"垃圾收集"的其他语言相比,你必须拥有一个对"谁拥有什么"有深刻理解的设计.您的设计应该让"简单明了"知道哪些对象存在,以及谁"拥有"它们.
一般来说,这意味着将对象的"创建"和"删除"集中到某种"父"中,从来没有任何问题,"这是谁的对象?" 和"你管理的对象是什么?"
例如,"Parent"类将分配它"需要"的成员对象,并且只有该父对象才会删除这些对象.父的析构函数确保删除这些成员,因此这些对象不会被意外地"留下".因此,父"知道"其成员对象,当父对象消失时,您知道成员对象也已消失.如果您的设计是强耦合的,那么成员对象可以引用父对象,但这是所有权语义的"深层结束"(通常不推荐).
有些设计可能非常复杂.但是,一般来说,所有权语义永远不应该是复杂的:你应该总是知道"谁拥有什么",因此,你应该总是知道"存在什么对象".
使用垃圾收集语言可以实现其他设计,其中对象可能是"自由代理",并且您相信垃圾收集器可以执行清理已丢失轨道的对象的"艰苦工作".(我不偏袒这些设计,但根据独特的问题域,让他们认为有时可以接受其他语言.)
如果您的C++设计依赖于这样的"自由代理"对象,您可以编写自己的垃圾收集器,或拥有"实用程序所有者"来清理它们.例如,MyFreeAgent构造函数可以将自己注册为MyGarbageCollector,并且MyFreeAgent析构函数将从注销中取消注册MyGarbageCollector.因此,MyFreeAgent实例可以被"清理" MyGarbageCollector,或者MyFreeAgent甚至可以用" delete this;" 自杀,你会知道它已经消失了(因为它的析构函数会从中取消注册MyGarbageCollector).在这种设计中,单MyGarbagageCollector个人总是"知道" MyFreeAgent存在哪些实例.
这样的设计是"池的深层",即使它们有效,也应极其谨慎使用,因为它们具有大规模解构系统的巨大潜力.(我用他们,但你得要确保问题老老实实权证这样的设计.)
Cla*_*dio 10
@charley是完全正确的,他很好地解释了这个理论.虽然在实践中这种所有权问题可能在许多场景中发生,但最常见的一种是在继承QT类时忘记调用基类的构造函数 - 当我从头开始编码时,我总是会遇到这种情况.
以这个非常常见的子类化QAbstractTableModel为例:
from PyQt4.QtCore import *
class SomeTableModel(QAbstractTableModel):
def __init__(self, filename):
super(SomeTableModel, self).__init__() # If you forget this, you'll get the
# "underlying C/C++ object has been
# deleted" error when you instantiate
# SomeTableModel.
Run Code Online (Sandbox Code Playgroud)
在目前接受的回答声称:
您无法测试基础对象是否存在(例如,是否已被“删除”)
错了 还有必须有办法做到这一点,否则这将是不可能的PyQt引发异常。以下是一些示例输出,演示了如何显式测试删除:
>>> import sip
>>> from PyQt4 import QtCore, QtGui
>>> app = QtGui.QApplication([''])
>>> w = QtGui.QWidget()
>>> w.setAttribute(QtCore.Qt.WA_DeleteOnClose)
>>> sip.isdeleted(w)
False
>>> w.close()
True
>>> sip.isdeleted(w)
True
>>> w.objectName()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: wrapped C/C++ object of type QWidget has been deleted
Run Code Online (Sandbox Code Playgroud)
PyQt小部件包含一个Python部分和一个C ++部分。如果Qt删除了C ++部分,则将留下一个空的Python包装器对象。并且,如果您尝试通过处于该状态的对象调用任何Qt方法,RuntimeError则将引发a(当然,它可能比段错误更可取)。
通常,Qt倾向于不隐式删除对象,这就是为什么我在上面的示例中必须显式标记要删除的小部件的原因。始终清楚记录该一般规则的例外情况-例如,通过指定是对象的所有权是Qt还是调用者。(这就是为什么即使您不了解C ++,熟悉Qt文档也可以真正付出的原因之一)。
在大多数PyQt代码中,避免问题的最简单方法是确保对象具有父对象,或者明确保留对对象的引用(通常作为主窗口的实例属性)。