QObject是Qt的基类
QObject object;
qDebug() << sizeof (object);
QObject的大小是8,除了虚函数表指针需要的4个字节以外,另外的4个字节是QScopedPointer d_ptr;
QObjectData
QObjectData中存储了Qt对象的基础数据
class Q_CORE_EXPORT QObjectData {
public:
virtual ~QObjectData() = 0;
QObject *q_ptr; //指向接口类
QObject *parent; //指向父对象
QObjectList children; //指向QObject相关的子类列表
uint isWidget : 1; //占用1bit的内存空间
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; //占用24bit的内存空间
int postedEvents;
QDynamicMetaObjectData *metaObject;
QMetaObject *dynamicMetaObject() const;
};
为什么要这样呢
原因是Qt中有一个很重要的设计模式就是句柄实体模式,也就是以QObject为基类的类一般都是句柄类,一般只有一个指针指向一个实体类,在实体类中保存全部的数据
而且一般情况下这个指针还是私有的,方便以后修改句柄类的实现细节
因此,也可以说和句柄类继承关系平行的也有一套实体类派生体系,因此,准确的说,Qt的基类其实有两个,一个是QObject,这是句柄类的唯一基类,另一个是QObjectData,这是实体
QObjectData中的 QObject *q_ptr;
QObject中的QScopedPointer d_ptr;
使得句柄类和实体类可以双向的引用,为什么是这样的命名方式呢?可能q指的是Qt接口类,d指的是Data数据类
QObject parent; //指向父对象
QObjectList children; //指向QObject相关的子类列表
这确实是个大胆的设计,如果系统中产生了1000000个QObject实例(对于大的系统,这个数字很容易达到吧),每个QObject子类平均下来是100(这个数字可能大了),
光这些指针的开销就有1000000100*4=400M,是够恐怖的,如果我们必须在灵活性和运行开销之间做一个选择的话,无疑Qt选择了前者,对此我也很难评论其中的优劣
再看一下QObject的构造函数
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);
Q_TRACE(QObject_ctor, this);
}
New QObjectPrivate 给d_ptr
d_ptr不是用来指向数据类QObjectData的吗
其实QObjectPrivate继承自QObjectData
QObjectData只是用来保存数据,QObjectPrivate类封装了线程处理,信号和槽机制等具体的实现,所以后面的比如QWidget的数据类QWidgetPrivate都是直接继承QObjectPrivate
为了方便接口类与数据类的访问,比如QWidget和QWidgetPrivate之间要互相访问但是保存的类型QObjectData和QObjectData都是基类要经过转换, 为了简单起见,Qt声明了两个宏
QObjectData中的 QObject *q_ptr;
QObject中的QScopedPointer d_ptr;
#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_DECLARE_PRIVATE(Class) \
inline Class##Private* d_func() \
{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr));) } \
inline const Class##Private* d_func() const \
{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr));) } \
friend class Class##Private;
只要在类的头文件中使用这两个宏,就可以通过函数直接得到实体类和句柄类的实际类型了,而且这里还声明了友元,使得数据类和句柄类连访问权限也不用顾忌了
而且为了在函数中使用方便,更是直接声明了以下两个宏
#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()
比如
QWindow *QWidget::windowHandle() const
{
Q_D(const QWidget);
return d->windowHandle();
}
void QWidgetPrivate::setModal_sys()
{
Q_Q(QWidget);
if (q->windowHandle())
q->windowHandle()->setModality(q->windowModality());
}
setObjectName
void QObject::setObjectName(const QString &name)
{
Q_D(QObject); //获取d指针
if (!d->extraData) //如果extraData为空 new一个extraData
d->extraData = new QObjectPrivate::ExtraData;
if (d->extraData->objectName != name) { //如果对象名不同 修改对象名
d->extraData->objectName = name;
emit objectNameChanged(d->extraData->objectName, QPrivateSignal()); //发射对象名改变信号
}
}