详解 Qt 源码分析 QObject

移动开发
本文介绍的是Qt 源码分析 QObject,代码一一实现,先来看内容。

Qt 源码分析 QObject是本文要介绍的内容,很详细的去解析,先来看内容。QtQObject

1.试验代码:

  1. #include <QApplication> 
  2. #include <QtCore> 
  3. #include <QtGui> 
  4. int main(int argc, char *argv[])  
  5. {  
  6.  QApplication app(argc, argv);  
  7.  int size = sizeof(QObject);  
  8.  QPushButton* quit = new QPushButton("Quit");  
  9.  delete quit;  
  10.  return app.exec();  

QObject是Qt类体系的唯一基类,就象MFC中的CObject和Dephi中的TObject,是Qt各种功能的源头活水,因此Qt源码分析的***节就放在这个QObject上

  1. int size = sizeof(QObject); 

QObject的大小是8,除了虚函数表指针需要的4个字节以外,另外的4个字节是:

  1. QObjectData *d_ptr; 

QObject中的数据被封装在QObjectData类中了,为什么要封装数据呢?

原因是Qt中有一个很重要的设计模式就是句柄实体模式,也就是以QObject为基类的类一般都是句柄类,一般只有一个指针指向一个实体类,在实体类中保存全部的数据

而且一般情况下这个指针还是私有的,方便以后修改句柄类的实现细节

因此,也可以说和句柄类继承关系平行的也有一套实体类派生体系,因此,准确的说,Qt的基类其实有两个,一个是QObject,这是句柄类的唯一基类,另一个是QObjectData,这是实体

类的基类

  1. QObjectData类定义如下:  
  2. class QObjectData {  
  3. public:  
  4.     virtual ~QObjectData() = 0;  
  5.     QObject *q_ptr;  
  6.     QObject *parent;  
  7.     QObjectList children;  
  8.  
  9.     uint isWidget : 1;  
  10.     uint pendTimer : 1;  
  11.     uint blockSig : 1;  
  12.     uint wasDeleted : 1;  
  13.     uint ownObjectName : 1;  
  14.     uint sendChildEvents : 1;  
  15.     uint receiveChildEvents : 1;  
  16.     uint unused : 25;  
  17.     int postedEvents;  
  18. #ifdef QT3_SUPPORT  
  19.     int postedChildInsertedEvents;  
  20. #else  
  21.     int reserved;  
  22. #endif  
  23. };  
  24. QObject *q_ptr; 

这个指针指向实体类对应的句柄类,这和上面的代码

  1. QObjectData *d_ptr; 

遥相呼应,使得句柄类和实体类可以双向的引用,为什么是这样的命名方式呢?可能q指的是Qt接口类,d指的是Data数据类,这当然是猜测了,但是或许可以方便你记忆,在Qt中,

这两个指针名字是非常重要的,必须记住

但是仅仅如此还是不容易使用这两个指针,因为它们都是基类的类型,难道每次使用都要类型转换吗?为了简单起见,Qt在这里声明了两个宏

  1. #define Q_DECLARE_PRIVATE(Class) \  
  2.     inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(d_ptr); } \  
  3.     inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(d_ptr); } \  
  4.     friend class Class##Private;  
  5.  
  6. #define Q_DECLARE_PUBLIC(Class) \  
  7.     inline Class* q_func() { return static_cast<Class *>(q_ptr); } \  
  8.     inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \  
  9.     friend class Class; 

只要在类的头文件中使用这两个宏,就可以通过函数直接得到实体类和句柄类的实际类型了,而且这里还声明了友元,使得数据类和句柄类连访问权限也不用顾忌了

而且为了cpp文件中调用的方便,更是直接声明了以下两个宏

  1. #define Q_D(Class) Class##Private * const d = d_func()  
  2. #define Q_Q(Class) Class * const q = q_func() 

好了,使用起来倒是方便了,但是以后局部变量可千万不能声明为d和q了

这里的d_func和q_func函数是非常常用的函数,可以理解为一个是得到数据类,一个是得到Qt接口类

  1. QObject *parent; 

这里指向QObject的父类

  1. QObjectList children; 

这里指向QObject相关的子类列表

这确实是个大胆的设计,如果系统中产生了1000000个QObject实例(对于大的系统,这个数字很容易达到吧),每个QObject子类平均下来是100(这个数字可能大了),

光这些指针的开销就有1000000*100*4=400M,是够恐怖的,如果我们必须在灵活性和运行开销之间做一个选择的话,无疑Qt选择了前者,对此我也很难评论其中的优劣,

还是祈求越来越强的硬件水平和Qt这么多年来得到的赫赫威名保佑我们根本就没有这个问题吧,呵呵

总之,Qt确实在内存中保存了所有类实例的树型结构

  1. uint isWidget : 1;  
  2. uint pendTimer : 1;  
  3. uint blockSig : 1;  
  4. uint wasDeleted : 1;  
  5. uint ownObjectName : 1;  
  6. uint sendChildEvents : 1;  
  7. uint receiveChildEvents : 1;  
  8. uint unused : 25; 

这些代码就简单了,主要是一些标记位,为了节省内存开销,这里采用了位域的语法,还保留了25位为unused,留做以后的扩充

  1. #ifdef QT3_SUPPORT  
  2.     int postedChildInsertedEvents;  
  3. #else  
  4.     int reserved;  
  5. #endif 

这里或许是为了兼容Qt3下序列化的数据吧,即使没有定义QT3_SUPPORT,还是保留了一个数据reserved,以保证整个QObjectData的大小不变

#p#

具体看一个例子吧,对这种句柄实体模式加深认识,这就是Qt中的按钮类QPushButton

  1. QPushButton的句柄类派生关系是:  
  2. QObject  
  3.  QWidget  
  4.   QAbstractButton  
  5.    QPushButton  
  6.      
  7. QPushButton的实体类派生关系是:  
  8. QObjectData  
  9.  QObjectPrivate  
  10.   QWidgetPrivate  
  11.    QAbstractButtonPrivate  
  12.     QPushButtonPrivate 

可以看出,这里确实是一个平行体系,只不过实体类派生关系中多了一个QObjectPrivate,这个类封装了线程处理,信号和槽机制等具体的实现,可以说它才是Qt实体类中

真正起作用的基类,而QObjectData不过是一层浅浅的数据封装而已

先不忙了解QObjectPrivate类中的接口和实现,我们先看看在Qt中,句柄类和实体类这两条体系是如何构造的?

  1. QPushButton* quit = new QPushButton("Quit"); 

创建一个Qt的按钮,简简单单一行代码,其实背后大有玄机

  1. QPushButton::QPushButton(const QString &text, QWidget *parent)  
  2.     : QAbstractButton(*new QPushButtonPrivate, parent) 

首先QPushButton的构造函数中调用了QAbstractButton的构造函数,同时马上new出来一个QPushButtonPrivate实体类,然后把指针转换为引用传递给QAbstractButton

  1. QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent)  
  2.     : QWidget(dd, parent, 0) 

QAbstractButton的构造函数中继续调用基类QWidget的构造函数,同时把QPushButtonPrivate实体类指针继续传给基类

  1. QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WFlags f)  
  2.     : QObject(dd, ((parent && (parent->windowType() == Qt::Desktop)) ? 0 : parent)), QPaintDevice() 

QWidget继续坐着同样的事情

  1. QObject::QObject(QObjectPrivate &dd, QObject *parent)  
  2.     : d_ptr(&dd) 

终于到了基类QObject,这里就直接把QPushButtonPrivate的指针赋值给了d_ptr(还记得这个变量名称吧)

最终在QPushButton构造时同时产生的new QPushButtonPrivate被写到了QObject中的d_ptr中

  1. QObject::QObject(QObjectPrivate &dd, QObject *parent)  
  2.     : d_ptr(&dd)  
  3. {  
  4.     Q_D(QObject);  
  5.     ::qt_addObject(d_ptr->q_ptr = this);  
  6.     QThread *currentThread = QThread::currentThread();  
  7.     d->thread = currentThread ? QThreadData::get(currentThread)->id : -1;  
  8.     Q_ASSERT_X(!parent || parent->d_func()->thread == d->thread, "QObject::QObject()",  
  9.                "Cannot create children for a parent that is in a different thread.");  
  10.     if (parent && parent->d_func()->thread != d->thread)  
  11.         parent = 0;  
  12.     if (d->isWidget) {  
  13.         if (parent) {  
  14.             d->parentparent = parent;  
  15.             d->parent->d_func()->children.append(this);  
  16.         }  
  17.         // no events sent here, this is done at the end of the QWidget constructor  
  18.     } else {  
  19.         setParent(parent);  
  20.     }  

然后执行QObject的构造函数,这里主要是一些线程的处理,先不理它

  1. QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WFlags f)  
  2.     : QObject(dd, ((parent && (parent->windowType() == Qt::Desktop)) ? 0 : parent)), QPaintDevice()  
  3. {  
  4.     d_func()->init((parent && parent->windowType() == Qt::Desktop ? parent : 0), f);  

然后是QWidget的构造函数,这里调用了数据类QWidgetPrivate的init函数,这个函数不是虚函数,因此静态解析成QWidgetPrivate的init函数调用

  1. QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent)  
  2.     : QWidget(dd, parent, 0)  
  3. {  
  4.     Q_D(QAbstractButton);  
  5.     d->init();  

然后是QAbstractButton的构造函数,这里调用了数据类QAbstractButton的init函数,这个函数不是虚函数,因此静态解析成QAbstractButton的init函数调用

  1. QPushButton::QPushButton(const QString &text, QWidget *parent)  
  2.     : QAbstractButton(*new QPushButtonPrivate, parent)  
  3. {  
  4.     Q_D(QPushButton);  
  5.     d->init();  
  6.     setText(text);  

然后是QPushButton的构造函数,这里调用了数据类QPushButton的init函数,这个函数不是虚函数,因此静态解析成QPushButton的init函数调用。

#p#

现在的事情很清楚了,总结一下:

QPushButton在构造的时候同时生成了QPushButtonPrivate指针,QPushButtonPrivate创建时依次调用数据类基类的构造函数

QPushButton的构造函数中显示的调用了基类的构造函数并把QPushButtonPrivate指针传递过去,QPushButton创建时依次调用接口类基类的构造函数

在接口类的构造函数中调用了平行数据类的init函数,因为这个函数不是虚函数,因此就就是此次调用了数据类的init函数

需要指出的是,为什么QPushButtonPrivate实体类指针要转换为引用呢?为什么不是直接传递指针?结论是人家喜欢这样写,就是不传指针传引用,而且要用一个*new之类的怪异语法,真叫人没有办法,其实这里用指针是一样的,代码看起来也自然一些.

  1. delete quit; 

说完了构造,再说说析构

  1. QPushButton::~QPushButton()  
  2. {  

这里当然会调用QPushButton的析构函数了

  1.  QAbstractButton::~QAbstractButton()  
  2. {  
  3. #ifndef QT_NO_BUTTONGROUP  
  4.     Q_D(QAbstractButton);  
  5.     if (d->group)  
  6.         d->group->removeButton(this);  
  7. #endif  

然后是QAbstractButton的析构函数

  1. QWidget::~QWidget()  
  2. {  
  3.     Q_D(QWidget);  
  4. ...  

然后是QWidget的析构函数,这里洋洋洒洒一大堆代码,先不管它

  1. QObject::~QObject()  
  2. {  
  3. ...  

***是QObject的析构函数,这里也是洋洋洒洒的一大堆

  1.     Q_D(QObject);  
  2.     if (d->wasDeleted) {  
  3. #if defined(QT_DEBUG)  
  4.         qWarning("Double QObject deletion detected");  
  5. #endif  
  6.         return;  
  7.     }  
  8.     d->wasDeleted = true

这些没有什么好说的,就是设一个wasDeleted的标志,防止再被引用,对于单线程情况下,马上就要被删除了,还搞什么标记啊,根本没用,但是对于多线程情况下,这个标记应该是有用的

  1. // set all QPointers for this object to zero  
  2. GuardHash *hash = ::guardHash();  
  3. if (hash) {  
  4.     QWriteLocker locker(guardHashLock());  
  5.     GuardHash::iterator it = hash->find(this);  
  6.     const GuardHash::iterator end = hash->end();  
  7.     while (it.key() == this && it != end) {  
  8.         *it.value() = 0;  
  9.         it = hash->erase(it);  
  10.     }  

这里是支持QPointers的实现代码,我们以后再说

  1. emit destroyed(this); 

Qt的一个指针删除时要发送destroyed信号,一般情况下是没有槽来响应的

  1. QConnectionList *list = ::connectionList();  
  2. if (list) {  
  3.     QWriteLocker locker(&list->lock);  
  4.     list->remove(this);  

这里清除了信号槽机制中的记录

  1. if (d->pendTimer) {  
  2.     // have pending timers  
  3.     QThread *thr = thread();  
  4.     if (thr || d->thread == 0) {  
  5.         // don't unregister timers in the wrong thread  
  6.         QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance(thr);  
  7.         if (eventDispatcher)  
  8.             eventDispatcher->unregisterTimers(this);  
  9.     }  

这里清除定时器

  1. d->eventFilters.clear(); 

这里清除事件过滤机制

  1. // delete children objects  
  2. if (!d->children.isEmpty()) {  
  3.     qDeleteAll(d->children);  
  4.     d->children.clear();  

这里清除所有子类指针,当然每个子类指针清除时又会清除它的所有子类,因此Qt中new出来的指针很少有显示对应的delete,因为只要最上面的指针被框架删除了,

它所连带的所有子类都被自动删除了

  1. {  
  2.     QWriteLocker locker(QObjectPrivate::readWriteLock());  
  3.     ::qt_removeObject(this);  
  4.  
  5.     /*  
  6.       theoretically, we cannot check d->postedEvents without  
  7.       holding the postEventList.mutex for the object's thread,  
  8.       but since we hold the QObjectPrivate::readWriteLock(),  
  9.       nothing can go into QCoreApplication::postEvent(), which  
  10.       effectively means noone can post new events, which is what  
  11.       we are trying to prevent. this means we can safely check  
  12.       d->postedEvents, since we are fairly sure it will not  
  13.       change (it could, but only by decreasing, i.e. removing  
  14.       posted events from a differebnt thread)  
  15.     */  
  16.     if (d->postedEvents > 0)  
  17.         QCoreApplication::removePostedEvents(this);  
  18. }  
  19. if (d->parent)        // remove it from parent object  
  20.     d->setParent_helper(0);  
  21. delete d;  
  22. d_ptr = 0

这里要删除相关的数据类指针了

小结:Qt 源码分析 QObject的内容介绍完了,希望本文对你有所帮助!

责任编辑:zhaolei 来源: 互联网
相关推荐

2011-06-23 14:05:32

Qt 事件机制

2011-06-23 13:38:27

QT 元对象 信号

2011-06-23 13:25:42

QT 源码 窗口

2011-06-22 14:47:51

QT 多线程 QObject

2009-07-08 13:22:30

JDK源码分析Set

2011-08-25 15:41:42

Lua源码

2011-06-23 11:16:39

Qt Excel

2011-06-20 11:14:09

Qt QxtGlobalS 热键

2011-06-23 14:27:48

QT QLibrary 动态库

2011-06-23 15:10:39

Qt 窗体

2011-06-24 10:05:51

QT 对象 父对象

2011-06-23 15:32:05

Qt Windows消息

2011-06-20 17:33:58

Qt MeegoTouch Maemo

2011-06-28 15:01:01

Qt PIMPL

2011-06-14 15:45:02

Qt Object

2022-03-07 10:05:02

SpringStreamMQ连接

2023-06-15 14:09:00

解析器Servlet容器

2011-06-20 14:27:57

Qt Embedded

2011-06-24 14:34:17

Qt 小票 打印

2011-04-02 09:17:38

点赞
收藏

51CTO技术栈公众号