Question

I have a setup with a main model (QStandardModel), a proxy model which changes the output of the DisplayRole, and a separate tableview displaying each model. Inside the main model data is a user role that stores a pointer to another QObject which is used by the proxy model to get the desired display value.

I'm running into problems when the object pointed to by that variable is deleted. I am handling deletion in the main model via the destroyed(QObject*) signal. Inside the slot, I search through the model looking for any items that are pointing to the object and delete the reference.

That part works fine on its own but I also have connected to the onDataChanged(...) signal of the proxy model, where I call resizeColumnsToContents() on the proxy model. This then calls the proxy's data() function. Here I check to see if the item has a pointer and, if it does, get some information from the object for display.

The result of all this becomes:

  1. Object about to be deleted triggers destroyed(...) signal
  2. Main model looks for any items using the deleted object and calls setData to remove the reference
  3. Tableview catches onDataChanged signal for the proxy model and resizes columns
  4. Proxy model's data(...) is called. It checks if the item in the main model has the object pointer and, if so, displays a value from the object. If not, it displays something else.

The problem is, at step 4 the item from the main model apparently still hasn't been deleted; the pointer address is still stored. The object the pointer was referencing, though, has been deleted by this point resulting in a segfault.

How can I fix my setup to make sure the main model is finished deleting pointer references before the proxy model tries to update?

Also, here is pseudo-code for the relevant sections:

// elsewhere
Object *someObject = new QObject();
QModelIndex index = mainModel->index(0,0);
mainModel->setData(index, someObject, ObjectPtrRole);
// do stuff
delete someObject; // Qt is actually doing this, I'm not doing it explicitly

// MainModel
void MainModel::onObjectDestroyed(QObject *obj)
{
  // iterating over all model items
    // if item has pointer to obj
      item->setData(QVariant::fromValue(NULL), ObjectPtrRole));
}

// receives onDataChanged signal
void onProxyModelDataChanged(...)
{
  ui->tblProxyView->reseizeColumnsToContents();
}

void ProxyModel::data(const QModelIndex &index, int role) const
{
  QModelIndex srcIndex = mapToSource(index);
  if(role == Qt::DisplayRole)
  {
    QVariant v = sourceModel()->data(srcIndex, ObjectPtrRole);
    Object *ptr = qvariant_cast<Object*>(v);
    if(ptr != NULL)
      return ptr->getDisplayData();
    else
      return sourceModel->data(srcIndex, role);
  }
}

The problem is ptr is not NULL, but the referenced object is deleted, at the time ProxyModel::data(...) is called so I end up with a segfault.

Was it helpful?

Solution

To avoid dangling pointer dereferences with instances of QObject, you can do one of two things:

  1. Use object->deleteLater - the object will be deleted once the control returns to the event loop. Such functionality is also known as autorelease pools.

  2. Use a QPointer. It will set itself to null upon deletion of the object, so you can check it before use.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top