Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are consequences of forcing QObject as a parent of QWidget?

Tags:

c++

qt

The following code compiles perfectly:

QObject* o = new QObject(0);
QWidget* w = new QWidget(0);
qobject_cast<QObject*>(w)->setParent(o);

I cannot legally set QObject as a parent of QWidget. But using qobject_cast it is possible. Are there negative consequences?

like image 366
rmflow Avatar asked Mar 11 '15 16:03

rmflow


1 Answers

What are consequences of forcing QObject as a parent of QWidget?

Irrevocable undefined behavior.

Qt is not designed to support a non-widget parent to a QWidget. I consider it an API bug in Qt, since a QWidget isn't fully a QObject in the Liskov Substitution Principle sense because of that limitation.

Qt 4.x will crash when attempting to activate the widget. So it'll work until you focus your application and then will crash.

Qt 5.x asserts in QObject::setParent().

The assertion can be bypassed, though:

// https://github.com/KubaO/stackoverflown/tree/master/questions/widget-parent-28992276
#include <QApplication>
#include <QLabel>

class ParentHacker : private QWidget {
public:
   static void setParent(QWidget * child_, QObject * parent) {
      // The following line invokes undefined behavior
      auto child = static_cast<ParentHacker*>(child_);
      Q_ASSERT(child->d_ptr->isWidget);
      child->d_ptr->isWidget = 0;
      child->QObject::setParent(parent);
      child->d_ptr->isWidget = 1;
   }
};

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   QLabel w{"Hello!"};
   w.setMinimumSize(200, 100);
   w.show();
   ParentHacker::setParent(&w, &app);
   return app.exec();
}

It will crash somewhere else then.

You'd be fighting an uphill battle trying to patch Qt to get it to work. It's not a worthwhile fight, I think - not unless a decision is made to make a QWidget truly-a QObject and change its constructor signature. That can be done at the earliest in Qt 6 since it's a binary-incompatible change AFAIK.

Moreover, what you're trying to do is mostly unnecessary. You can certainly have a hidden QWidget parent to multiple stand-alone top-level widgets.

#include <QApplication>
#include <QLabel>

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   QWidget parent;
   QLabel l1{"Close me to quit!"}, l2{"Hello!"};
   for (auto label : {&l1, &l2}) {
      label->setMinimumSize(200, 100);
      label->setParent(&parent);
      label->setWindowFlags(Qt::Window);
      label->setText(QString("%1 Parent: %2.").
                     arg(label->text()).arg((quintptr)label->parent(), 0, 16));
      label->show();
   }
   l2.setAttribute(Qt::WA_QuitOnClose, false);
   return app.exec();
}

The overhead of having the widget hidden is minimal, you're not wasting any resources by using a QWidget instead of a QObject for the parent.

like image 100
Kuba hasn't forgotten Monica Avatar answered Nov 12 '22 09:11

Kuba hasn't forgotten Monica