目錄
- d指標和q指標
- 簡單示例
- q指標
- QObject和QObjectPrivate
- qtcreator中的變體1
- qtcreator中的變體2
- 小結
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 = https://www.cnblogs.com/codeForFamily/p/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指標進行賦值,
對于第二個受保護的建構式,具體干什么用的,留給大家自行學習了,提示一下,可以前往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的建構式中初始化,這樣,在Foo的成員函式中,也能直接使用d指標,
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句柄呼叫介面,
小結
其實,只要掌握了原理,各種變化就隨意了,
- 對于管理類來說,一般是單例模式的,這種情況下,我們可以在cpp檔案中定義靜態的m_instance和d,如變體1,
- 對于非管理類,譬如QObject,可以創建類的多個實體的,我們一般需要在公有類中把私有類指標d作為成員變數,如簡單示例,
最后,提醒一點,如果在cpp中使用Q_OBJECT,請注意先使用moc工具創建xx_moc.cpp,并#include到該cpp中,否則會報錯的,"undefined reference to vtable",
原創造福大家,共享改變世界
獻出一片愛心,溫暖作者心靈
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/10517.html
標籤:其他
