自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

詳解 Qt 源碼分析 QObject

移動(dòng)開發(fā)
本文介紹的是Qt 源碼分析 QObject,代碼一一實(shí)現(xiàn),先來看內(nèi)容。

Qt 源碼分析 QObject是本文要介紹的內(nèi)容,很詳細(xì)的去解析,先來看內(nèi)容。QtQObject

1.試驗(yàn)代碼:

  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源碼分析的***節(jié)就放在這個(gè)QObject上

  1. int size = sizeof(QObject); 

QObject的大小是8,除了虛函數(shù)表指針需要的4個(gè)字節(jié)以外,另外的4個(gè)字節(jié)是:

  1. QObjectData *d_ptr; 

QObject中的數(shù)據(jù)被封裝在QObjectData類中了,為什么要封裝數(shù)據(jù)呢?

原因是Qt中有一個(gè)很重要的設(shè)計(jì)模式就是句柄實(shí)體模式,也就是以QObject為基類的類一般都是句柄類,一般只有一個(gè)指針指向一個(gè)實(shí)體類,在實(shí)體類中保存全部的數(shù)據(jù)

而且一般情況下這個(gè)指針還是私有的,方便以后修改句柄類的實(shí)現(xiàn)細(xì)節(jié)

因此,也可以說和句柄類繼承關(guān)系平行的也有一套實(shí)體類派生體系,因此,準(zhǔn)確的說,Qt的基類其實(shí)有兩個(gè),一個(gè)是QObject,這是句柄類的唯一基類,另一個(gè)是QObjectData,這是實(shí)體

類的基類

  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; 

這個(gè)指針指向?qū)嶓w類對(duì)應(yīng)的句柄類,這和上面的代碼

  1. QObjectData *d_ptr; 

遙相呼應(yīng),使得句柄類和實(shí)體類可以雙向的引用,為什么是這樣的命名方式呢?可能q指的是Qt接口類,d指的是Data數(shù)據(jù)類,這當(dāng)然是猜測了,但是或許可以方便你記憶,在Qt中,

這兩個(gè)指針名字是非常重要的,必須記住

但是僅僅如此還是不容易使用這兩個(gè)指針,因?yàn)樗鼈兌际腔惖念愋?難道每次使用都要類型轉(zhuǎn)換嗎?為了簡單起見,Qt在這里聲明了兩個(gè)宏

  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; 

只要在類的頭文件中使用這兩個(gè)宏,就可以通過函數(shù)直接得到實(shí)體類和句柄類的實(shí)際類型了,而且這里還聲明了友元,使得數(shù)據(jù)類和句柄類連訪問權(quán)限也不用顧忌了

而且為了cpp文件中調(diào)用的方便,更是直接聲明了以下兩個(gè)宏

  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函數(shù)是非常常用的函數(shù),可以理解為一個(gè)是得到數(shù)據(jù)類,一個(gè)是得到Qt接口類

  1. QObject *parent; 

這里指向QObject的父類

  1. QObjectList children; 

這里指向QObject相關(guān)的子類列表

這確實(shí)是個(gè)大膽的設(shè)計(jì),如果系統(tǒng)中產(chǎn)生了1000000個(gè)QObject實(shí)例(對(duì)于大的系統(tǒng),這個(gè)數(shù)字很容易達(dá)到吧),每個(gè)QObject子類平均下來是100(這個(gè)數(shù)字可能大了),

光這些指針的開銷就有1000000*100*4=400M,是夠恐怖的,如果我們必須在靈活性和運(yùn)行開銷之間做一個(gè)選擇的話,無疑Qt選擇了前者,對(duì)此我也很難評(píng)論其中的優(yōu)劣,

還是祈求越來越強(qiáng)的硬件水平和Qt這么多年來得到的赫赫威名保佑我們根本就沒有這個(gè)問題吧,呵呵

總之,Qt確實(shí)在內(nèi)存中保存了所有類實(shí)例的樹型結(jié)構(gòu)

  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; 

這些代碼就簡單了,主要是一些標(biāo)記位,為了節(jié)省內(nèi)存開銷,這里采用了位域的語法,還保留了25位為unused,留做以后的擴(kuò)充

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

這里或許是為了兼容Qt3下序列化的數(shù)據(jù)吧,即使沒有定義QT3_SUPPORT,還是保留了一個(gè)數(shù)據(jù)reserved,以保證整個(gè)QObjectData的大小不變

#p#

具體看一個(gè)例子吧,對(duì)這種句柄實(shí)體模式加深認(rèn)識(shí),這就是Qt中的按鈕類QPushButton

  1. QPushButton的句柄類派生關(guān)系是:  
  2. QObject  
  3.  QWidget  
  4.   QAbstractButton  
  5.    QPushButton  
  6.      
  7. QPushButton的實(shí)體類派生關(guān)系是:  
  8. QObjectData  
  9.  QObjectPrivate  
  10.   QWidgetPrivate  
  11.    QAbstractButtonPrivate  
  12.     QPushButtonPrivate 

可以看出,這里確實(shí)是一個(gè)平行體系,只不過實(shí)體類派生關(guān)系中多了一個(gè)QObjectPrivate,這個(gè)類封裝了線程處理,信號(hào)和槽機(jī)制等具體的實(shí)現(xiàn),可以說它才是Qt實(shí)體類中

真正起作用的基類,而QObjectData不過是一層淺淺的數(shù)據(jù)封裝而已

先不忙了解QObjectPrivate類中的接口和實(shí)現(xiàn),我們先看看在Qt中,句柄類和實(shí)體類這兩條體系是如何構(gòu)造的?

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

創(chuàng)建一個(gè)Qt的按鈕,簡簡單單一行代碼,其實(shí)背后大有玄機(jī)

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

首先QPushButton的構(gòu)造函數(shù)中調(diào)用了QAbstractButton的構(gòu)造函數(shù),同時(shí)馬上new出來一個(gè)QPushButtonPrivate實(shí)體類,然后把指針轉(zhuǎn)換為引用傳遞給QAbstractButton

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

QAbstractButton的構(gòu)造函數(shù)中繼續(xù)調(diào)用基類QWidget的構(gòu)造函數(shù),同時(shí)把QPushButtonPrivate實(shí)體類指針繼續(xù)傳給基類

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

QWidget繼續(xù)坐著同樣的事情

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

終于到了基類QObject,這里就直接把QPushButtonPrivate的指針賦值給了d_ptr(還記得這個(gè)變量名稱吧)

最終在QPushButton構(gòu)造時(shí)同時(shí)產(chǎn)生的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.     }  

然后執(zhí)行QObject的構(gòu)造函數(shù),這里主要是一些線程的處理,先不理它

  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的構(gòu)造函數(shù),這里調(diào)用了數(shù)據(jù)類QWidgetPrivate的init函數(shù),這個(gè)函數(shù)不是虛函數(shù),因此靜態(tài)解析成QWidgetPrivate的init函數(shù)調(diào)用

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

然后是QAbstractButton的構(gòu)造函數(shù),這里調(diào)用了數(shù)據(jù)類QAbstractButton的init函數(shù),這個(gè)函數(shù)不是虛函數(shù),因此靜態(tài)解析成QAbstractButton的init函數(shù)調(diào)用

  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的構(gòu)造函數(shù),這里調(diào)用了數(shù)據(jù)類QPushButton的init函數(shù),這個(gè)函數(shù)不是虛函數(shù),因此靜態(tài)解析成QPushButton的init函數(shù)調(diào)用。

#p#

現(xiàn)在的事情很清楚了,總結(jié)一下:

QPushButton在構(gòu)造的時(shí)候同時(shí)生成了QPushButtonPrivate指針,QPushButtonPrivate創(chuàng)建時(shí)依次調(diào)用數(shù)據(jù)類基類的構(gòu)造函數(shù)

QPushButton的構(gòu)造函數(shù)中顯示的調(diào)用了基類的構(gòu)造函數(shù)并把QPushButtonPrivate指針傳遞過去,QPushButton創(chuàng)建時(shí)依次調(diào)用接口類基類的構(gòu)造函數(shù)

在接口類的構(gòu)造函數(shù)中調(diào)用了平行數(shù)據(jù)類的init函數(shù),因?yàn)檫@個(gè)函數(shù)不是虛函數(shù),因此就就是此次調(diào)用了數(shù)據(jù)類的init函數(shù)

需要指出的是,為什么QPushButtonPrivate實(shí)體類指針要轉(zhuǎn)換為引用呢?為什么不是直接傳遞指針?結(jié)論是人家喜歡這樣寫,就是不傳指針傳引用,而且要用一個(gè)*new之類的怪異語法,真叫人沒有辦法,其實(shí)這里用指針是一樣的,代碼看起來也自然一些.

  1. delete quit; 

說完了構(gòu)造,再說說析構(gòu)

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

這里當(dāng)然會(huì)調(diào)用QPushButton的析構(gòu)函數(shù)了

  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的析構(gòu)函數(shù)

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

然后是QWidget的析構(gòu)函數(shù),這里洋洋灑灑一大堆代碼,先不管它

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

***是QObject的析構(gòu)函數(shù),這里也是洋洋灑灑的一大堆

  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

這些沒有什么好說的,就是設(shè)一個(gè)wasDeleted的標(biāo)志,防止再被引用,對(duì)于單線程情況下,馬上就要被刪除了,還搞什么標(biāo)記啊,根本沒用,但是對(duì)于多線程情況下,這個(gè)標(biāo)記應(yīng)該是有用的

  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的實(shí)現(xiàn)代碼,我們以后再說

  1. emit destroyed(this); 

Qt的一個(gè)指針刪除時(shí)要發(fā)送destroyed信號(hào),一般情況下是沒有槽來響應(yīng)的

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

這里清除了信號(hào)槽機(jī)制中的記錄

  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.     }  

這里清除定時(shí)器

  1. d->eventFilters.clear(); 

這里清除事件過濾機(jī)制

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

這里清除所有子類指針,當(dāng)然每個(gè)子類指針清除時(shí)又會(huì)清除它的所有子類,因此Qt中new出來的指針很少有顯示對(duì)應(yīng)的delete,因?yàn)橹灰钌厦娴闹羔槺豢蚣軇h除了,

它所連帶的所有子類都被自動(dòng)刪除了

  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

這里要?jiǎng)h除相關(guān)的數(shù)據(jù)類指針了

小結(jié):Qt 源碼分析 QObject的內(nèi)容介紹完了,希望本文對(duì)你有所幫助!

責(zé)任編輯:zhaolei 來源: 互聯(lián)網(wǎng)
相關(guān)推薦

2011-06-23 14:05:32

Qt 事件機(jī)制

2011-06-23 13:38:27

QT 元對(duì)象 信號(hào)

2011-06-23 13:25:42

QT 源碼 窗口

2009-07-08 13:22:30

JDK源碼分析Set

2011-08-25 15:41:42

Lua源碼

2011-06-20 11:14:09

Qt QxtGlobalS 熱鍵

2011-06-23 11:16:39

Qt Excel

2011-06-22 14:47:51

QT 多線程 QObject

2011-06-23 14:27:48

QT QLibrary 動(dòng)態(tài)庫

2011-06-24 10:05:51

QT 對(duì)象 父對(duì)象

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-23 15:10:39

Qt 窗體

2011-06-14 15:45:02

Qt Object

2011-06-17 10:19:11

Qt QWidge QSetting

2011-06-24 10:54:34

Qt Mysql

2011-06-17 09:58:26

Qt Chapter QObject

2011-07-04 16:12:00

QT QWidget

2011-06-24 12:58:49

Qt LineEdit
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)