Kam*_*jii 9 c++ inheritance qt qwidget qt-signals
我想我在这里遇到了一种钻石继承问题.
Qt提供了几个旋转框,用于整数值,用于双精度以及日期/时间.它们都来自QAbstractSpinBox:
#include <QtWidgets/QSpinBox>
class QSpinBox:
public QAbstractSpinBox {
};
#include <QtWidgets/QDoubleSpinBox>
class QDoubleSpinBox:
public QAbstractSpinBox {
};
Run Code Online (Sandbox Code Playgroud)
现在我想添加一些通用于所有旋转框的功能,在这个具体的例子中,一个按钮将旋转框恢复到最小(因此是specialValueText).所以我也从中衍生出来QAbstractSpinBox并想出了这样的东西:
class AbstractRevertibleSpinBox:
public QAbstractSpinBox {
public:
RevertibleSpinBox() {
/* Use abstract base: */
QAction *revertAction = new QAction(this);
QAbstractSpinBox::lineEdit()->addAction(
revertAction, QLineEdit::TrailingAction);
/* ... */
}
public slots:
virtual void revert() = 0;
}
Run Code Online (Sandbox Code Playgroud)
这revert()包含应该实现如何恢复不同旋转框的纯净.例如,setValue(double)用于QDoubleSpinBox或setDate(QDate)用于QDateEdit.然后我以明显的方式为我需要的所有旋转框派生了相应的类,如下所示:
class RevertibleSpinBox:
public QSpinBox,
public AbstractRevertibleSpinBox {
protected:
void revert() {
/* Revert 'int' */
setValue(0);
}
};
class RevertibleDoubleSpinBox:
public QDoubleSpinBox,
public AbstractRevertibleSpinBox {
protected:
void revert() {
/* Revert 'double' */
setValue(0.0);
}
};
Run Code Online (Sandbox Code Playgroud)
这显然不起作用,因为QAbstractSpinBox中的任何内容现在都不明确.我以为我可以使用虚拟继承解决它,如果,例如,这将工作QDoubleSpinBox将几乎从自身派生QAbstractSpinBox.但事实并非如此.此外,它会因QObject而失败,因为Qt似乎在static_cast那里做了很多挫折,这也不适用于虚拟继承.我还考虑通过将AbstractRevertibleSpinBox传递独特的旋转框类型的模板类作为模板类参数来解析它.然后构造将如下所示:
template<class Base>
class AbstractRevertibleSpinBox:
public Base {};
class RevertibleSpinBox:
public AbstractRevertibleSpinBox<SpinBox> { };
Run Code Online (Sandbox Code Playgroud)
这可行,但Qt的moc对模板类非常不满意.因此,例如,我无法连接模板类中的任何信号和插槽.至少不使用传统的基于字符串的SIGNAL()/SLOT()语法.
有没有其他合理优雅的方法来克服这个问题..?
正如我在评论中所指出的那样,如果你想要一个易于扩展的特征系统,我认为这是装饰器模式的一个明显的例子,否则只是从QObject继承,而不是从基本的"接口"继承几乎相同的代码.
我将从恕我直言的更糟糕的方法开始,在其他答案中提供:
这显然是令人厌烦的,甚至更重要的是,您将无法支持任何QSpinBox子类,因为您总是需要为每个添加创建一个新的子类.这只是一种不灵活的方法.
这看起来像是两个不同事物的不必要的耦合,因此如果您通过按钮之后以任何其他方式触发它们,您将无法轻松地重复使用旋转框.我认为这两个概念应该保持不同并单独管理.
此外,dynamic_cast如你所知,你应该使用ing是错误的qobject_cast.
让我们仔细看看装饰器的方法:

这还不是您的案例的解决方案,但它很好地展示了如何将功能添加(即"装饰")到现有层次结构中.为了更加具体地了解您的用例,让我们看看您的特定场景中会出现什么:
组件:QAbstractSpinBox
具体组件
装饰者:AbstractSpinBoxDecorator(在你的情况下可以省略这一步)
具体装饰:RevertibleSpinBoxDecorator
让我们实现这个设计:
#include <QAbstractSpinBox>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QDateTimeEdit>
#include <QDateEdit>
#include <QTimeEdit>
#include <QPushButton>
#include <QApplication>
#include <QMainWindow>
#include <QHBoxLayout>
#include <QWidget>
#include <QShowEvent>
class RevertibleSpinBoxDecorator : public QAbstractSpinBox
{
Q_OBJECT
public:
explicit RevertibleSpinBoxDecorator(QAbstractSpinBox *abstractSpinBox, QAbstractSpinBox *parent = Q_NULLPTR)
: QAbstractSpinBox(parent)
, m_abstractSpinBox(abstractSpinBox)
{
}
public slots:
void revert(bool)
{
QSpinBox *spinBox = qobject_cast<QSpinBox*>(m_abstractSpinBox);
if (spinBox) {
spinBox->setValue(spinBox->minimum());
return;
}
QDoubleSpinBox *doubleSpinBox = qobject_cast<QDoubleSpinBox*>(m_abstractSpinBox);
if (doubleSpinBox) {
doubleSpinBox->setValue(doubleSpinBox->minimum());
return;
}
QDateEdit *dateEdit = qobject_cast<QDateEdit*>(m_abstractSpinBox);
if (dateEdit) {
dateEdit->setDate(dateEdit->minimumDate());
return;
}
QTimeEdit *timeEdit = qobject_cast<QTimeEdit*>(m_abstractSpinBox);
if (timeEdit) {
timeEdit->setTime(timeEdit->minimumTime());
return;
}
QDateTimeEdit *dateTimeEdit = qobject_cast<QDateTimeEdit*>(m_abstractSpinBox);
if (dateTimeEdit) {
dateTimeEdit->setDateTime(dateTimeEdit->minimumDateTime());
return;
}
Q_ASSERT_X(false, "decorator", "concrete component unimplemented");
}
protected:
void showEvent(QShowEvent *event) Q_DECL_OVERRIDE
{
m_abstractSpinBox->show();
event->ignore();
hide();
}
private:
QAbstractSpinBox *m_abstractSpinBox;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = Q_NULLPTR) : QMainWindow(parent)
{
connect(pushButton, &QPushButton::clicked, revertibleSpinBoxDecorator, &RevertibleSpinBoxDecorator::revert);
QHBoxLayout *layout = new QHBoxLayout(centralWidget);
layout->addWidget(revertibleSpinBoxDecorator);
layout->addWidget(pushButton);
setCentralWidget(centralWidget);
}
private:
QWidget *centralWidget{new QWidget(this)};
QDoubleSpinBox *doubleSpinBox{new QDoubleSpinBox(this)};
RevertibleSpinBoxDecorator *revertibleSpinBoxDecorator{new RevertibleSpinBoxDecorator(doubleSpinBox)};
QPushButton *pushButton{new QPushButton(this)};
};
#include "main.moc"
int main(int argc, char **argv)
{
QApplication application(argc, argv);
MainWindow mainWindow;
mainWindow.show();
return application.exec();
}
Run Code Online (Sandbox Code Playgroud)
如果你想摆脱QAbstractSpinBox继承,你将需要更多的胶水和恕我直言,以获得不多的收益,同时失去灵活性.你会从这样的事情开始:
#include <QAbstractSpinBox>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QDateTimeEdit>
#include <QDateEdit>
#include <QTimeEdit>
#include <QPushButton>
#include <QApplication>
#include <QMainWindow>
#include <QHBoxLayout>
#include <QWidget>
#include <QShowEvent>
class RevertibleSpinBoxDecorator : public QObject
{
Q_OBJECT
public:
explicit RevertibleSpinBoxDecorator(QAbstractSpinBox *abstractSpinBox, QObject *parent = Q_NULLPTR)
: QObject(parent)
, m_abstractSpinBox(abstractSpinBox)
{
}
public slots:
void revert(bool)
{
QSpinBox *spinBox = qobject_cast<QSpinBox*>(m_abstractSpinBox);
if (spinBox) {
spinBox->setValue(spinBox->minimum());
return;
}
QDoubleSpinBox *doubleSpinBox = qobject_cast<QDoubleSpinBox*>(m_abstractSpinBox);
if (doubleSpinBox) {
doubleSpinBox->setValue(doubleSpinBox->minimum());
return;
}
QDateEdit *dateEdit = qobject_cast<QDateEdit*>(m_abstractSpinBox);
if (dateEdit) {
dateEdit->setDate(dateEdit->minimumDate());
return;
}
QTimeEdit *timeEdit = qobject_cast<QTimeEdit*>(m_abstractSpinBox);
if (timeEdit) {
timeEdit->setTime(timeEdit->minimumTime());
return;
}
QDateTimeEdit *dateTimeEdit = qobject_cast<QDateTimeEdit*>(m_abstractSpinBox);
if (dateTimeEdit) {
dateTimeEdit->setDateTime(dateTimeEdit->minimumDateTime());
return;
}
Q_ASSERT_X(false, "strategy", "strategy not implemented");
}
private:
QAbstractSpinBox *m_abstractSpinBox;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = Q_NULLPTR) : QMainWindow(parent)
{
connect(pushButton, &QPushButton::clicked, revertibleSpinBoxDecorator, &RevertibleSpinBoxDecorator::revert);
QHBoxLayout *layout = new QHBoxLayout(centralWidget);
layout->addWidget(doubleSpinBox);
layout->addWidget(pushButton);
setCentralWidget(centralWidget);
}
private:
QWidget *centralWidget{new QWidget(this)};
QDoubleSpinBox *doubleSpinBox{new QDoubleSpinBox(this)};
RevertibleSpinBoxDecorator *revertibleSpinBoxDecorator{new RevertibleSpinBoxDecorator(doubleSpinBox)};
QPushButton *pushButton{new QPushButton(this)};
};
#include "main.moc"
int main(int argc, char **argv)
{
QApplication application(argc, argv);
MainWindow mainWindow;
mainWindow.show();
return application.exec();
}
Run Code Online (Sandbox Code Playgroud)
TEMPLATE = app
TARGET = main
QT += widgets
CONIG += c++11
SOURCES += main.cpp
Run Code Online (Sandbox Code Playgroud)
qmake && make && ./main
Run Code Online (Sandbox Code Playgroud)