我担心在将来升级Qt 5.9下开发的小部件库时,无需重新编译已经使用它的代码。当然,我已经开始使用 PImpl 习语,其Qt版本描述了here和here。
然而,在尝试调整我的代码时,我想出了这样的想法,即我可以使用带有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的评论。
答案 0 :(得分:3)
我想出了这个想法,而不是添加新的数据成员并将它们移动到一个单独的私人类[...]
这是错误的思考方式。该接口没有数据成员。无论您拥有什么成员,都可以直接进入PIMPL。你没有“移动”任何东西,你不会将它们放在错误的地方开始。
此外,与父对象具有相同生命周期的成员的堆分配是过早的悲观化。将它们按值存储在PIMPL中。
[...]我可以使用带有lambda函数的Qt信号/槽机制,并且只有局部变量
如果你需要存储超过QObject
个孩子的东西而不滥用财产系统,这将无效。
这不是一种灵活的方法,并且正确地做到这一点并不困难。 Qt建立了所有必要的模式。有关详细信息,请参阅this question。
您不打算从中派生的类不需要单独的Class_p.h
标头。您可以将ClassPrivate
定义添加到Class.cpp
文件本身的开头。
// Foo.h
#include <QWidget>
class FooPrivate;
class Foo : public QWidget {
Q_OBJECT
Q_DECLARE_PRIVATE(Foo)
QScopedPointer<FooPrivate> const d_ptr;
public:
explicit Foo(QWidget *parent = {});
~Foo();
protected:
Foo(FooPrivate &, QWidget *parent = {}); // for expansion
};
// Bar.h
#include "Foo.h"
class BarPrivate;
class Bar : public Foo {
Q_OBJECT
Q_DECLARE_PRIVATE(Bar)
Q_PROPERTY(int data READ data)
public:
explicit Bar(QWidget *parent = {});
~Bar();
int data() const;
protected:
Bar(BarPrivate &, QWidget *parent = {}); // for expansion
};
// Foo_p.h
#include "Foo.h"
class FooPrivate {
Q_DECLARE_PUBLIC(Foo)
Q_DISABLE_COPY(Foo) // usually desired
Foo * const q_ptr;
public:
QVBoxLayout m_layout{q_ptr};
QPushButton m_button{q_ptr->tr("Hello!")};
QLineEdit m_lineEdit;
QCheckBox m_checkBox{q_ptr->tr("Active")};
void on_pushButtonClicked();
void on_checkBoxStateChanged(int state);
explicit FooPrivate(Foo *);
virtual ~FooPrivate() {} // we're meant to be derived from!
};
// Bar_p.h
#include "Foo_p.h"
#include "Bar.h"
class BarPrivate : public FooPrivate {
Q_DECLARE_PUBLIC(Bar)
public:
int m_data = 44;
explicit BarPrivate(Bar *);
};
// Foo.cpp
#include "Foo_p.h"
Foo::Foo(QWidget * parent) :
Foo(*new FooPrivate(this), parent)
{}
Foo::Foo(FooPrivate & d_ptr, QWidget * parent) :
QWidget(parent),
d_ptr(d_ptr)
{}
Foo::~Foo() {}
FooPrivate::FooPrivate(Foo * q_ptr) :
q_ptr(q_ptr)
{
m_layout.addWidget(&m_button);
m_layout.addWidget(&m_lineEdit);
m_layout.addWidget(&m_checkBox);
connect(&m_button, &QPushButton::clicked, [=]{ on_pushButtonClicked(); });
connect(&m_checkBox, &QCheckBox::stateChanged, [=](int s){ on_checkBoxStateChanged(s); });
}
// Bar.cpp
#include "Bar_p.h"
Bar::Bar(QWidget * parnet) :
Bar(*new BarPrivate(this), parent)
{}
Bar::Bar(BarPrivate & d_ptr, QWidget * parent) :
Foo(d_ptr, parent)
{}
Bar::~Bar() {}
BarPrivate::BarPrivate(Bar * q_ptr) :
FooPrivate(q_ptr)
{}
int Bar::data() const {
Q_D(const Bar);
return d->m_data;
}