欢迎加QQ群309798848交流C/C++/linux/Qt/音视频/OpenCV
源码面前,了无秘密。阅读源码能帮助我们理解实现原理,然后更灵活的运用。
接下来我用VS2015调试Qt5.9源码。
首先提一下,Qt在WinMain中调用用户的main函数:
我们知道,Qt的类以QObject为首,QWidget,QLabel之类的都是其子类(直接或间接继承)。
那么先来看看QObject的源码,我在下图打了个断点:
void debugQtSource()
{
QObject* obj = new QObject;
}
F11进入,
QObject::QObject(QObject *parent)
: d_ptr(new QObjectPrivate)
{
Q_D(QObject);
d_ptr->q_ptr = this;
d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current();
d->threadData->ref();
if (parent) {
QT_TRY {
if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData))
parent = 0;
setParent(parent);
} QT_CATCH(...) {
d->threadData->deref();
QT_RETHROW;
}
}
#if QT_VERSION < 0x60000
qt_addObject(this);
#endif
if (Q_UNLIKELY(qtHookData[QHooks::AddQObject]))
reinterpret_cast<QHooks::AddQObjectCallback>(qtHookData[QHooks::AddQObject])(this);
}
可以看到关键是d_ptr(d_pointer)
d_ptr声明:QScopedPointer<QObjectData> d_ptr;
QScopedPointer的思想是RAII,作用是用户不需要手动delete。
QObjectPrivate继承QObjectData
QObjectData定义:
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 deleteLaterCalled : 1;
uint unused : 24;
int postedEvents;
QDynamicMetaObjectData *metaObject;
QMetaObject *dynamicMetaObject() const;
};
QObjectData有一个指针q_ptr,指向QObject。
通过d_ptr(new QObjectPrivate);和d_ptr->q_ptr = this; 可以知道QObject和QObjectData都有一个指针互相指向对方。
接下来先看一个关系图:
Qt实现采用了d-pointer这种方式。
使用d-pointer的目的:
实现二进制兼容,头文件与实现细节无关。Qt started out closed source?
二进制兼容:即Qt(dll)版本的更新替换不会影响应用程序exe。因为Qt的接口类(QObject等)的实例的大小是固定的。
在Qt中,QObject,QWidget,QLabel这些都是接口类,真正的实现在QObjectPrivate,QWidgetPrivate,QLabelPrivate 等Private类中。
QObjec的子类,例如QWidget,将QWidgetPrivate*传给QObject的d_ptr。同理QObjectData的子类,例如QWidgetPrivate将QWidget*传给QObjectData的q_ptr。也就是是说着两个类继承树中,各自维护一个指向对方的指针。
但是有一个问题,d_ptr是QObjectData*,我需要将他转为QWidgetPrivate*才能调用QWidgetPrivate的函数或数据。每次使用的时候都要转换指针很麻烦。考虑到代码复用,Qt采用了宏Q_D和Q_Q。
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(); }
#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;
#define Q_DECLARE_PRIVATE_D(Dptr, Class) \
inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(Dptr)); } \
inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(Dptr)); } \
friend class Class##Private;
#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;
#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()
如上,Q_D将d_ptr从QObjectData*转换为QWidgetPrivate*,然后赋值给d
Q_Q将q_ptr从QObject*转换为QWidget*,然后赋值给q