qt - 在父窗口/小部件类中初始化子小部件的目的是啥?

Posted

技术标签:

【中文标题】qt - 在父窗口/小部件类中初始化子小部件的目的是啥?【英文标题】:qt - what's the purpose of initialising child widgets in parent window/widget class?qt - 在父窗口/小部件类中初始化子小部件的目的是什么? 【发布时间】:2014-02-23 05:38:31 【问题描述】:

在 Qt 中的 VideoWidget 和 Secure Socket Client 示例中,那里显示的代码初始化父窗口小部件中的子窗口小部件,如下所示:

SslClient::SslClient(QWidget *parent)
: QWidget(parent), socket(0), padLock(0), executingDialog(false)

VideoPlayer::VideoPlayer(QWidget *parent)
: QWidget(parent)
, mediaPlayer(0, QMediaPlayer::VideoSurface)
, playButton(0)
, positionSlider(0)
, errorLabel(0)

但是,在代码的下方,我看到以下内容:

playButton = new QPushButton;

或者在安全套接字客户端的情况下,这个:

padLock = new QToolButton;

在代码中初始化时,为什么要在构造函数中初始化?

【问题讨论】:

这似乎是对语法的误解:***.com/questions/2785612/… @Peter,什么?我认为 OP 完全理解冒号,但质疑初始化列表中 padLock(0)padLock = new ...; 的重复 @AlexisWilke,我编辑了这个问题,因为说继承,我的意思是初始化。 :o 【参考方案1】:

在代码中初始化时,为什么要在构造函数中初始化?

这样实现是异常安全的。假设您有以下代码:

SslClient::SslClient(QWidget *parent)
: QWidget(parent), socket(0), padLock((QToolButton*)(0x0BEEF)), executingDialog(false) 
  throw std::exception();
  padLock = new QToolButton;

析构函数将删除padLock,但它具有垃圾值,并且您有未定义的行为。回想一下,删除 nullptr 是安全的(就像在 C 中调用 free(NULL) 一样!)。垃圾挂锁值显示当您不初始化它时会发生什么。抛出表明一些中间代码可能会抛出。具体来说,如果分配不能成功,任何介入的new 都会抛出。 new 不返回失败(如:它抛出 std::bad_alloc 而不是返回,因此返回值的概念根本不适用)。

如果一个人正在编写惯用的 C++,那么指针不应该是裸指针,而是 std::unique_ptrQScopedPointer,然后这个问题就消失了。您不必记得将指针初始化为零,也不必记得清理它。 RAII 为您带来很多胜利。这就是当你真正按照应有的方式使用 C++ 时获得的秘密武器。

C++ RAII 习语本身不存在于任何其他常见的编程语言中。在允许它的语言(例如 Java、C#、F#、Python、OCaml 和 Common Lisp)中,惯用的解决方法是定义一个高阶 with_resource 函数,请参阅 OCaml、Java 的示例和Python 和Python again。基本上,在 C++ 以外的语言中,尤其是在垃圾收集语言中,内存资源释放的处理方式与非内存资源释放不同。在 C++ 中,它们统一在 RAII 保护伞下。

【讨论】:

好久没做C++了。现在不太确定,至少我不能给出链接。对 cme​​ts 感到抱歉(已经删除了它们)。 @MareInfinitus 从构造函数中抛出是正常的。应该假设从析构函数中抛出相当于调用terminate(),并且只有在事情明显无法恢复的糟糕情况下才必须这样做(比如内存损坏)。 请注意,Qt 不使用异常,并且示例中也没有异常处理(正确地)。 The C++ RAII idiom doesn't exist per se in any other common programming language -> 实际上,它确实如此,例如 python。事实上,C++ 没有完整的 RAII 习语,不像 python。 @LaszloPapp Qt 无法不使用异常:当new 失败时,它会抛出异常,并且相当多的关键核心 Qt 代码似乎被编写为在发生这种情况时不会泄漏。 AFAIK,python 需要 with 构造来防止资源泄漏 - 还是有什么我不知道的?我的 Python-fu 非常初级。 做我的客人,然后告诉我在这种情况下 qt 在哪里抛出异常。 :) 我在示例中也没有看到任何非 qt 异常代码。此外,在 99% 的情况下,内存分配失败不是问题,特别是因为 Qt 不能在真正的资源关键系统上运行。因此,我个人认为这是不好的做法。请注意,在未来,这个问题将随着统一初始化声明中的就地初始化而消失。至于 python,你需要有一个明确的结构来获取和释放。 C++ 并没有真正具有适当的 RAII。【参考方案2】:

这是一个很好的做法。你应该总是初始化你的变量,即使它看起来没用。

这也适用于局部变量。这是一个很好的做法。

实际上,如果你这样做:

QString str;

然后str自动初始化。不幸的是,如果你这样做:

int value;

然后 value 不会被初始化为任何东西。这对速度有好处,但通常会导致错误。

在构造函数的情况下,这是一个好主意,因为如果您更改主体中的代码,您最终可能不会初始化一个变量或另一个......这至少可以在列表中设置为 null初始化器。

与异常联系起来也是有原因的,但这可能超出了 Qt 的范围,Qt 并没有太多使用异常。

【讨论】:

我不确定这是否真的是一种“好”的做法。我想说,这只是其中一种做法。例如,内核开发人员可能会说这是“坏”做法。 ;-) 是的。一些开发人员认为初始化是浪费时间。尽管在许多情况下,编译器会通过尽可能删除第一次初始化来“修复”问题。因此,int value(0); 后跟 value = 9; 将在 value 的位置一步删除 9。 可以,但标准不保证,具体看实现。【参考方案3】:

在代码中初始化时,为什么要在构造函数中初始化?

我相信这只是因为如果实际实例化稍后推迟到不同的方法,例如,您不会在某些操作中间获得未初始化的成员,从而导致未定义的行为。

但是,在这种特殊情况下,如果不打算很快修改代码,那将没有多大意义。

这也可能归结为不同项目中的编码风格,但在这种特殊情况下,我不知道这会受到官方 Qt 编码风格的限制。例如,在 linux 内核中,他们更喜欢 not 像这样初始化它。

【讨论】:

以上是关于qt - 在父窗口/小部件类中初始化子小部件的目的是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Qt半透明背景导致子小部件在父小部件中“印记”

pyqt - 将来自 qt 设计器的子小部件定位在父级附近

如何在 Qt 表单类中调整 OpenGL 小部件的大小

Qt 删除窗口小部件

QT:如何关闭同一个小部件的多个窗口?

其他非 qt 窗口顶部的 Qt 小部件