强制 QObject 作为 QWidget 的父级有什么后果?

2021-12-09 00:00:00 qt c++

以下代码完美编译:

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

我不能合法地将 QObject 设置为 QWidget 的父级.但是使用 qobject_cast 是可能的.是否有负面影响?

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

推荐答案

强制 QObject 作为 QWidget 的父级有什么后果?

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

不可撤销的未定义行为.

Irrevocable undefined behavior.

Qt 并非旨在支持 QWidget 的非小部件父级.我认为这是 Qt 中的一个 API 错误,因为由于这种限制,QWidget 在 Liskov 替换原则意义上并不完全是 QObject.

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 在尝试激活小部件时会崩溃.所以它会一直工作,直到你专注于你的应用程序,然后会崩溃.

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 在 QObject::setParent() 中断言.

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

虽然可以绕过断言:

// 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.

您会在尝试修补 Qt 以使其正常工作时进行一场艰苦的战斗.我认为这不是一场值得的斗争 - 除非决定使 QWidget 真正成为 QObject 并更改其构造函数签名.这最早可以在 Qt 6 中完成,因为它是一个二进制不兼容的更改 AFAIK.

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.

此外,您尝试做的事情大多是不必要的.您当然可以为多个独立的顶级小部件设置一个隐藏的 QWidget 父级.

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();
}

隐藏小部件的开销很小,您不会通过使用 QWidget 而不是 QObject 为父级浪费任何资源.

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.

相关文章