我很担心在Qt 5.9下开发的小部件库可能在将来升级,而无需重新编译已经使用它的代码。当然,我已经开始使用这个成语,并且其中描述的Qt版本为here和here。然而,当我试图调整我的代码时,我想出了一个想法,即不是添加新的数据成员,而是将它们移动到一个单独的私有类,我可以使用Qt的带有lambda函数的信号/插槽机制,并且只有局部变量。让我们来具体说明这个思想与下面的例子:是否使用C++ Lambda函数作为Qt中的槽帮助保存库的二进制兼容性?
方案A:
class Foo : public QWidget
{
Q_OBJECT
public:
explicit Foo(QWidget *parent = nullptr);
private:
// A bunch of data members
QPushButton *m_button;
QLineEdit *m_lineEdit;
QCheckBox *m_checkBox;
QString m_str;
private slots:
void on_pushButtonClicked();
void on_checkBoxStateChanged(int state);
};
Foo::Foo(QWidget *parent) :
QWidget(parent),
m_button(new QPushButton("Click me", this));
m_lineEdit(new QLineEdit(this)),
m_checkBox(new QCheckBox(this)),
m_str("Initial text")
{
connect(button, &QPushButton::clicked, this, &Foo::on_pushButtonClicked);
connect(checkBox, &QCheckBox::stateChanged, this, &Foo::on_checkBoxStateChanged);
}
Foo::on_pushButtonClicked()
{
m_str = m_lineEdit->text();
m_lineEdit->setDisabled(m_checkBox->isChecked());
}
Foo::on_checkBoxStateChanged(int state)
{
m_button->setText(state == Qt::Checked ? m_str : "Click me")
}
方案B:
class Foo : public QWidget
{
Q_OBJECT
public:
explicit Foo(QWidget *parent = nullptr);
};
Foo::Foo(QWidget *parent) : QWidget(parent)
{
QPushButton *button = new QPushButton("Click me", this);
QLineEdit *lineEdit = new QLineEdit(this);
QCheckBox *checkBox = new QCheckBox(this);
QString str("Initial text");
connect(button, &QPushButton::clicked, [=](){
str = lineEdit->text();
lineEdit->setDisabled(checkBox->isChecked());
});
connect(checkBox, &QCheckBox::stateChanged, [=](int state){
button->setText(state == Qt::Checked ? str : "Click me")
});
}
所以,变型B - 除了是更紧凑它不包含任何类数据成员,所以没有隐藏变量,因此不需要D指针。二进制兼容性仍然保证虽然(或是否?),如果将来构造函数重新实现了额外的本地变量使用相同的信号/插槽方式。我是否认为这样做会行得通,或者这种方法根本无法解决问题?
注意:有关在Qt中使用lambdas作为插槽的更多信息,请查看@Igor Tandetnik的评论here。
我不认为这会编译。 lambda中'str'将是const。即使它编译完成,请注意,按值捕获:在这两个不同的lambda中,“str”指的是不同的独立对象。你必须用堆分配的指针来保存所有的东西 - 一个类固醇上的'pimpl'。 –
@IgorTandetnik,我有你的观点。然而,这个构造函数的唯一重新实现不应该破坏二进制兼容性,应该如何呢? – scopchanov
我不太清楚“二进制兼容性”在这里的含义。只要您不修改定义了'Foo'类的头文件,您就不需要使用'Foo'重新编译源文件,如果这就是您要求的。也就是说,你会很难用Variant B的风格编写任何重要的代码。例如,在一个普通的类中,你可以有一个私人成员函数,你可以从多个地方调用。你打算如何重用代码?在构造函数中为每个“成员函数”创建一个lambda,并让所有连接处理函数捕获它?它会很快变得非常尴尬。 –