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

Posted

技术标签:

【中文标题】强制 QObject 作为 QWidget 的父级有啥后果?【英文标题】:What are consequences of forcing QObject as a parent of QWidget?强制 QObject 作为 QWidget 的父级有什么后果? 【发布时间】:2015-03-11 16:34:43 【问题描述】:

以下代码编译完美:

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

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

【问题讨论】:

只是好奇:这种层次结构的原因是什么? 什么?!? QWidget 公开继承自 QObject QWidget::setParent(QWidget*) 隐藏QObject::setParent(QObject*) static_cast 会更合适。或w-&gt;QObject::setParent(o);. 对于那些投票以“不清楚”来结束此内容的人 - 请不要。这是一个非常清楚的问题!一个相关是***.com/q/1585998/1329652 - 但我不认为它们是重复的。 【参考方案1】:

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

不可撤销的未定义行为。

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

Qt 4.x 在尝试激活小部件时会崩溃。所以它会一直工作,直到你专注于你的应用程序然后会崩溃。

Qt 5.x 在QObject::setParent() 中断言。

但是可以绕过断言:

// https://github.com/KubaO/***n/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 appargc, argv;
   QLabel w"Hello!";
   w.setMinimumSize(200, 100);
   w.show();
   ParentHacker::setParent(&w, &app);
   return app.exec();

然后它会在其他地方崩溃。

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

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

#include <QApplication>
#include <QLabel>

int main(int argc, char ** argv) 
   QApplication appargc, 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 作为父级,您不会浪费任何资源。

【讨论】:

以上是关于强制 QObject 作为 QWidget 的父级有啥后果?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以为 QObject 的父级使用共享指针?

(QNativeSocketEngine)QObject:无法为不同线程中的父级创建子级

QSystemTrayIcon in 4.8 - 如何删除这个东西?

从 C++ 设置创建的 qml 对象的父级不起作用

PyQt4 & flask:无法为不同线程中的父级创建子级

Qt构造函数的参数:QObject *parent = Q_NULLPTR