d指针
Qt对于数据的封装算比较高明的了,其实原理就是把接口和实现分开,类似于如下结构:
class A
{
class APrivate;
APrivate *pData;
public:
A();
~A();
...
}
这样,接口类A只负责描述希望调用者看到的,而真正的细节都在APrivate类中,当然这是我个人的理解,Qt的实现要复杂的多。
以QWidget为例
先看代码:
QWidget::QWidget(QWidget *parent, Qt::WindowFlags f)
: QObject(*new QWidgetPrivate, 0), QPaintDevice()
{
QT_TRY {
d_func()->init(parent, f);
} QT_CATCH(...) {
QWidgetExceptionCleaner::cleanup(this, d_func());
QT_RETHROW;
}
}
这个是QWidget的构造函数,接着往下看:
/*!
\internal
*/
QObject::QObject(QObjectPrivate &dd, QObject *parent)
: d_ptr(&dd)
{
Q_D(QObject);
d_ptr->q_ptr = this;
...
}
这个是QObject的构造函数,看到有个d_ptr
,类型为QScopedPointer<QObjectData>
,现在好像明白了什么,在QWidget
的构造函数中new
了一个QWidgetPrivate
对象,然后保存到基类的d_ptr
中。
这里有两个宏定义,可以获取当前类的d指针,就是Q_D
,实现如下:
#define Q_D(Class) Class##Private * const d = d_func()
这里有个d_func()
,这时什么?不急,在Q_D
定义的上方,就有一个Q_DECLARE_PRIVATE
定义:
#define Q_DECLARE_PRIVATE(Class) \
inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \
inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } \
friend class Class##Private;
接着就是:
template <typename T> static inline T *qGetPtrHelper(T *ptr) { return ptr; }
template <typename Wrapper> static inline typename Wrapper::pointer qGetPtrHelper(const Wrapper &p) { return p.data(); }
现在应该明白了,Qt里面细节都放在了Class##Private
的类中,也算是一种定好的命名格式。
Q_DECLARE_PRIVATE
这个是在类声明的时候调用Q_DECLARE_PRIVATE(QWidget)
。
只要在使用d指针之前,加上Q_D(QWidget);
就可以了。
q指针
与d指针相反,q指针是给Class##Private
类调用的,是获取接口类的指针,同样有一个Q_Q
宏定义:
#define Q_Q(Class) Class * const q = q_func()
同样,需要在Class##Private
类声明出,调用Q_DECLARE_PUBLIC
:
#define Q_DECLARE_PUBLIC(Class) \
inline Class* q_func() { return static_cast<Class *>(q_ptr); } \
inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \
friend class Class;
更多细节可还要继续研究。