dポインターとqポインター
クラスメンバー名に dポインターを導入し、dポインターを使用しています。
これは、バイナリ互換性を損なうことなくクラスに新しいプライベートデータメンバーを追加するための素晴らしいトリックです。さらに、ヘッダーファイルをクリーンに保ち、特定の実装を非表示にしてコンパイルを高速化することもできます。
簡単な例
// foo.h
class FooPrivate;
class Foo
{
public:
Foo();
int getData() const;
void setData(int d);
private:
FooPrivate* d;
};
// foo.cpp
class FooPrivate {
public:
FooPrivate() : data(0) {}
int data;
};
Foo::Foo() : d(new FooPrivate) {}
int Foo::getData() const { return d->data; }
void Foo::setData(int d) { d->data = d; }
インターフェースのみがFooクラスで公開され、特定の実装とデータはcppファイルのFooPrivateクラスで非表示になります。
qポインター
dポインターは、パブリッククラス内の対応するプライベートクラスにアクセスするために使用されます。同様に、qポインターは、プライベートクラスのリバースアクセサーに対応するパブリッククラスに使用されます。
上記のコードに簡単な変更を加えることができます
// foo.cpp
class FooPrivate {
public:
FooPrivate(Foo *f) : data(0), q(f) {}
int data;
Foo *q;
};
Foo::Foo() : d(new FooPrivate(this)) {}
QObject和QObjectPrivate
// qobject.h
class Q_CORE_EXPORT QObjectData {
public:
virtual ~QObjectData() = 0;
QObject *q_ptr;
QObject *parent;
QObjectList children;
uint isWidget : 1;
uint blockSig : 1;
uint wasDeleted : 1;
uint isDeletingChildren : 1;
uint sendChildEvents : 1;
uint receiveChildEvents : 1;
uint isWindow : 1; //for QWindow
uint unused : 25;
int postedEvents;
QDynamicMetaObjectData *metaObject;
QMetaObject *dynamicMetaObject() const;
};
class Q_CORE_EXPORT QObject
{
Q_OBJECT
Q_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged)
Q_DECLARE_PRIVATE(QObject)
public:
Q_INVOKABLE explicit QObject(QObject *parent=Q_NULLPTR);
....
protected:
QObject(QObjectPrivate &dd, QObject *parent = Q_NULLPTR);
protected:
QScopedPointer<QObjectData> d_ptr;
....
}
// qobject_p.h
class Q_CORE_EXPORT QObjectPrivate : public QObjectData
{
Q_DECLARE_PUBLIC(QObject)
...
}
QObjectのd_ptrはdポインターであり、QObjectDataのq_ptrはqポインターです。
qobject.hはユーザーに提供される表示ヘッダーファイルで、qobject_p.hは内部プライベートヘッダーファイルです。
簡単な例では、cppファイルでプライベートクラスを定義しましたが、プライベートクラスが大きすぎる場合は、ヘッダーファイルを個別に定義してcppファイルで参照できます。
// qobject.cpp
#include "qobject.h"
#include "qobject_p.h"
...
QObject::QObject(QObject *parent)
: d_ptr(new QObjectPrivate)
{
Q_D(QObject);
d_ptr->q_ptr = this;
...
}
QObject::QObject(QObjectPrivate &dd, QObject *parent)
: d_ptr(&dd)
{
Q_D(QObject);
d_ptr->q_ptr = this;
...
}
上記のコンストラクターの内容は自明であり、dポインターとqポインターに値を割り当てます。
2番目の保護されたコンストラクターの具体的な使用方法は、すべての人に任せて自分で学習してください。プロンプトは、qwidget.cppに移動して表示し、派生クラスの最後の基本クラスを割り当てるために使用できます。
存在するかどうかはわかりません。いくつかの特別なマクロ、Q_DECLARE_PUBLIC、Q_DECLARE_PRIVATE、Q_D、そして実際にはQ_Qがあります。これらのマクロは、qglobal.hで表示できます。最後の役割は、dポインターとqポインターへのアクセスを容易にすることです。関数でQ_DまたはQ_Qを宣言した後、d変数とq変数を直接使用して、dポインターとqポインターを置き換えることができます。
dポインターとqポインターの詳細については、https://wiki.qt.io/D-Pointerを参照してください。
qtcreatorのバリアント1
qtcreatorはプラグインメカニズムを使用しているため、公開されたコンポーネントのかなりの部分がシングルトンモードになっています。ハンドルを取得するには、次のモードを使用します。
static T *instance();
したがって、いくつかのバリアントが作成されました。次に例を示します。
// foo.h
class FooPrivate;
class Foo
{
friend class FooPrivate;
public:
static Foo *instance();
private:
Foo();
~Foo();
...
};
// foo_p.h
class Foo;
class FooPrivate {
public:
FooPrivate(Foo *qf) : q(qf) {}
private:
Foo *q;
...
};
// foo.cpp
#include "foo.h"
#include "foo_p.h"
...
static FooPrivate *d = 0;
static Foo *m_instance = 0;
Foo *Foo::instance()
{
return m_instance;
}
Foo::Foo()
{
m_instance = this;
d = new FooPrivate(this);
}
Foo::~Foo()
{
delete d;
d = 0;
m_instance = 0;
}
ここでの主な変更は、dポインターがFooのメンバーではなくなったことです。FooとFooPrivateはどちらも、cppで定義され、Fooのコンストラクターで初期化される静的変数です。このように、dポインターはFooのメンバー関数で直接使用することもできます。
qtcreator 2のバリアント
さらに、バリアントがあります。ユーザーが呼び出すインターフェースクラスのみを公開する場合は、特定の実装クラスを非表示にし、他の管理クラスの関連するメンバー関数を介してインターフェースクラスポインターを返すことができます。
// foo.h
class Foo
{
public:
void algo() = 0;
}
// foo_p.h
class FooPrivate : public Foo
{
public:
void algo() override;
protect:
void doAlgo() { }
}
// foo.cpp
void FooPrivate::algo() { doAlgo(); }
void FooPrivate::doAlgo() { }
// foomanager.h
class FooManager
{
static Foo *foo();
}
foo.hとfoomanager.hのみをユーザーに提供し、詳細と特定の実装をカプセル化します。ユーザーは、FooManagerの関数を介してfooハンドルを取得し、fooハンドルを介してインターフェイスを呼び出すことができます。
まとめ
実際、原則を習得している限り、さまざまな変更は任意です。
- 管理クラスの場合、通常はシングルトンモードですが、この場合、バリアント1などの静的なm_instanceとdをcppファイルで定義できます。
- QObjectなどの非マネージクラスの場合、クラスの複数のインスタンスを作成できます。通常、単純な例のように、プライベートクラスポインターdをパブリッククラスのメンバー変数として使用する必要があります。
最後に、cppでQ_OBJECTを使用する場合、最初にmocツールを使用してxx_moc.cppを作成し、#includeをcppに追加するように注意してください。そうしないと、「vtableへの未定義の参照」というエラーが報告されます。
原创造福大家,共享改变世界
献出一片爱心,温暖作者心灵