Понимание ошибки «базовый объект C / C ++ был удален» - PullRequest
19 голосов
/ 14 мая 2011

Я не первый раз получаю RuntimeError: underlying C/C++ object has been deleted.Я решал это много раз, изменяя свой код случайным, но интуитивно понятным способом, но теперь я снова сталкиваюсь с этим и просто не понимаю, почему это происходит ... То, о чем я прошу, - это общий подход к решению этой проблемы и ее устранению.

Я не буду публиковать здесь примеры кода, потому что мой проект слишком сложен, и я просто не могу понять, где ошибка.А также потому, что я прошу универсальное решение не только для этого случая.

Почему могут быть удалены «лежащие в основе C / C ++» объекты?
Как этого избежать?
Какпроверить, существует ли базовый объект?

Ответы [ 5 ]

13 голосов
/ 15 мая 2011

Вы не можете проверить, существует ли базовый объект (например, был ли он «удален»). Есть некоторая «хитрость», которую вы можете использовать для «своего рода» помощи, но я не рекомендую ее. Скорее, IMHO, я предполагаю, что вашему дизайну не хватает строгой семантики владения. (Это может быть необходимо в зависимости от сложности проблемы и вашего домена, но я бы рекомендовал против этого, если этого можно избежать.)

Ваша проблема - семантика владения. Особенно в C ++, в отличие от других языков, которые используют «сборку мусора» в памяти, вы ДОЛЖНЫ иметь проект, в котором вы хорошо понимаете, «кому что принадлежит». Ваш дизайн должен сделать «легким и очевидным», чтобы знать, какие объекты существуют, и кто «владеет» ими.

Как правило, это означает централизацию «создания» и «удаления» объектов в «родителя» какого-либо рода, где никогда не возникает вопроса: «Чей это объект?» и "Какими объектами вы управляете?"

Например, класс «Parent» будет выделять объекты-члены, которые ему «нужны», и только этот родитель удаляет эти объекты. Деструктор родителя гарантирует, что эти члены будут удалены, поэтому эти объекты не будут случайно «оставлены». Таким образом, родитель «знает» свои объекты-члены, и когда родитель исчезает, вы ЗНАЕТЕ, что объекты-члены также исчезли. Если ваш проект сильно связан, то объекты-члены могут ссылаться на родительский объект, но это «глубокий конец пула» для семантики владения (и обычно не рекомендуется).

Некоторые проекты на законных основаниях могут быть очень сложными. Однако в общем случае семантика владения НИКОГДА не должна быть сложной: вы должны ВСЕГДА знать, «кому что принадлежит», и, следовательно, вы должны ВСЕГДА знать, «какие объекты существуют».

Возможны другие конструкции с языками сборки мусора, где объекты могут быть «свободными агентами», и вы доверяете сборщику мусора выполнять «тяжелую работу» по очистке объектов, о которых вы потеряли отслеживание. (Я не неравнодушен к этим проектам, но допускаю, что они иногда приемлемы на этих других языках, основываясь на уникальных проблемных областях.)

Если ваша конструкция на C ++ основана на таких объектах «свободных агентов», вы можете написать свой собственный сборщик мусора или собственный «владелец утилиты» для их очистки. Например, конструктор MyFreeAgent может зарегистрировать себя в MyGarbageCollector, а деструктор MyFreeAgent отменит свою регистрацию в MyGarbageCollector. Таким образом, MyFreeAgent экземпляры могут быть "очищены" с помощью MyGarbageCollector, или MyFreeAgent может даже убить себя с помощью "delete this;", и вы ЗНАЕТЕ, что оно исчезло (потому что его деструктор отменит свою регистрацию от MyGarbageCollector). В этом проекте один MyGarbagageCollector ВСЕГДА "знал бы", что существует MyFreeAgent экземпляров.

Такие конструкции являются «глубоким концом пула» и должны использоваться с особой осторожностью, даже если они работают, потому что они имеют большой потенциал для массовой деструктуризации вашей системы. (Я использовал их, но вы получили , чтобы убедиться, что проблема честно оправдывает такой дизайн.)

10 голосов
/ 05 июня 2012

@ Чарли совершенно прав, он очень хорошо объяснил теорию.Хотя на практике эта проблема владения может возникать во многих сценариях, одним из наиболее распространенных является забывание вызова конструктора базового класса при создании подкласса класса 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.
5 голосов
/ 15 октября 2016

В настоящее время принятый ответ утверждает, что:

Вы не можете проверить, существует ли базовый объект (например, был ли он "удален")

Это неправильно. должен быть способом сделать это, иначе 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

Виджет PyQt состоит из части Python и части C ++.Если часть C ++ удаляется с помощью Qt, пустой объект оболочки Python останется позади.И если вы попытаетесь вызвать какие-либо методы Qt через объект в этом состоянии, будет поднят RuntimeError (что, конечно, предпочтительнее вероятного segfault).

В общем, Qt не пытается неявно удалитьобъекты, именно поэтому мне пришлось явно пометить виджет для удаления в примере выше.Исключения из этого общего правила всегда четко документированы, например, путем указания, является ли Qt или вызывающий объект владельцем объекта.(Это одна из причин, почему стоит ознакомиться с документацией Qt, даже если вы не знаете C ++).

В большинстве кодов PyQt самый простой способ избежать проблем - это убедиться, чтообъект имеет родителя или явно хранит ссылку на объект (часто в качестве атрибута экземпляра главного окна).

1 голос
/ 05 апреля 2013

Начиная с Python 3+, вы можете еще больше упростить код.

from PyQt4.QtCore import *

class SomeTableModel(QAbstractTableModel):
  def __init__(self, filename):
    super().__init__()   # No longer need to specify class name, or self

Super также предоставляет некоторые дополнительные меры безопасности, упомянутые здесь: Понимание Python super () с помощью методов __init __ ()

0 голосов
/ 14 октября 2016

Как проверить, существует ли базовый объект?

Как этого избежать?

Вы можете использовать:

try: someObject.objectName()
except RuntimeError: return False

Тест .objectName() - это произвольный запрос, который должны легко пройти все действительные объекты QObject.
Если они потерпят неудачу, они выбросят RuntimeError, что указывает на то, что они недействительны.
В этом грубом примере мы принимаем RuntimeError и вместо этого возвращаем false.

...