在子类中覆盖基类信号时连接到基类的信号

Connecting to a base class' signal when overriding it in a subclass

本文关键字:信号 基类 连接 子类 覆盖      更新时间:2023-10-16

Qt提供了QSpinBox,这是提供信号的远QObject导数:

class QSpinBox:
    public /* ... */ QObject {
    signals:
        void valueChanged(int i);
};

从这个旋转框派生时,我可能会为此信号提供一个覆盖,以发出该值的一些美化版本:

class MySpinBox:
    public QSpinBox {
    private slots:
        void originalValueChanged(int i) {
            /* Beautify */
            emit valueChanged( 42 * i );
        }
    signals:
        void valueChanged(int myPersonalI);
};

这似乎很好:信号MySpinBox::valueChanged(int)隐藏了继承的信号。唯一缺少的一点是将继承的QSpinBox::valueChanged(int)连接到专用插槽MySpinBox::originalValueChanged(int)

虽然使用信号和时隙语法可以做到这一点:

connect(
    static_cast<QSpinBox *>(this), &QSpinBox::valueChanged,
    this, &MySpinBox::originalValueChanged);

我既不确定这是否允许明智,也不确定如何使用传统的基于字符串的信号/插槽语法连接信号。使用后者,插槽显然连接到MySpinBox::originalValueChanged(int)信号,这显然不是故意的。

目的

上面的例子被简化为我认为我面临的问题。为了理解为什么我会遇到这个问题,也许是为了引导我摆脱这个(假装的(破碎的设计,想想一个QDoubleSpinBox导数来输入一些物理量,例如速度。用户可能希望以某个首选单位输入此内容,该单位可能是km/hmph。为了从应用程序中获取单位转换逻辑,我编写了一个新的小部件,该小部件派生自QWidget,仅包含一个QDoubleSpinBox。然后我实现了通常的旋转盒方法(setMinimumsetSingleStep等(,这些方法采用 SI 维度的相应属性(即本例中的 m/s(,将它们转换为某个选定的单位,然后配置较差的双旋转盒。

setValue(double)将其参数转换为用户的单位,并将其传递给下级。反过来,有一个valueChanged(double)信号,每当劣质旋转盒发出valueChanged,但数量转换回SI维度时就会发出。

所以最后我修修补补的是,不是用劣质的旋转盒来编写QWidget导数,而是从QDoubleSpinBox派生并重新实现。

利弊似乎部分清楚,包括下面斯特拉霍夫@Pavel解释的问题。实际上,编写小部件可以让我摆脱错误继承的方法被调用(由于不是虚拟的(,代价是包装QWidget。因此,作为中间步骤,我正在修补从QDoubleSpinBox派生私有。这当然不是 GUI 应用程序中的资源问题,但对我来说,这也是学习和修补的问题。

然后我偶然发现了这个信号,想知道Qt会怎么想。

请注意,QSpinBox::valueChanged 不是虚拟的,这意味着如果QSpinBox*指针中存储了MySpinBox的实例并调用其 valueChanged 方法,则将执行QSpinBox::valueChanged而不是MySpinBox::valueChanged。Qt内部函数肯定会有QSpinBox*指向实例的指针。这种不一致可能是错误的根源。非虚函数不应该在子类中重新定义,你不应该这样做。Qt内置类都没有使用这样的重新定义。子类通常会添加新信号,而基类信号保持不变。