Qobject in QT source code analysis

Source: Internet
Author: User

Http://blog.csdn.net/oowgsoo/article/details/1529284

Qobject of QT

1. TestCode:
# Include <qapplication>
# Include <qtcore>
# Include <qtgui>

Int main (INT argc, char * argv [])
{
Qapplication app (argc, argv );

Int size = sizeof (qobject );

Qpushbutton * Quit = new qpushbutton ("quit ");
Delete quit;

Return app.exe C ();
}

Qobject is the only base class of the QT class system. Like cobject in MFC and tobject in dephi, it is the source of various QT functions. Therefore, the first section of QT source code analysis is placed on this qobject.
Int size = sizeof (qobject );
The qobject size is 8, except for the four bytes required by the virtual function table pointer, the other four bytes are:
Qobjectdata * d_ptr;
The data in qobject is encapsulated in the qobjectdata class. Why should we encapsulate the data?
The reason is that a very important design pattern in QT is the handle entity pattern. classes based on qobject are generally handle classes. Generally, only one pointer points to one entity class, save all data in the object class
In general, this pointer is private to facilitate future modification of the Implementation Details of the handle class.
Therefore, it can be said that there is also a set of entity class derivation Systems parallel to the inheritance relationship of the handle class. Therefore, to be precise, there are actually two QT base classes, one being qobject, this is the unique base class of the handle class, And qobjectdata is the entity.
Base class of the class

The qobjectdata class is defined as follows:
Class qobjectdata {
Public:
Virtual ~ Qobjectdata () = 0;
Qobject * q_ptr;
Qobject * parent;
Qobjectlist children;

Uint iswidget: 1;
Uint pendtimer: 1;
Uint blocksig: 1;
Uint wasdeleted: 1;
Uint ownobjectname: 1;
Uint sendchildevents: 1;
Uint receivechildevents: 1;
Uint unused: 25;
Int postedevents;
# Ifdef qt3_support
Int postedchildinsertedevents;
# Else
Int reserved;
# Endif
};
Qobject * q_ptr;
This Pointer Points to the handle class corresponding to the object class, which is similar to the above Code
Qobjectdata * d_ptr;
In this way, the handle class and entity class can be referenced in two directions. Why is this naming method? Maybe Q refers to the QT interface class, and D refers to the data class. Of course, this is a guess, but it may be easier for you to remember. In QT,
These two pointer names are very important and must be remembered.

However, it is not easy to use these two pointers because they are all the types of the base class. Do you need to convert the types every time you use them? For the sake of simplicity, QT declares two macros here

# Define q_declare_private (class )/
Inline class # private * d_func () {return reinterpret_cast <class # private *> (d_ptr );}/
Inline const class # private * d_func () const {return reinterpret_cast <const class # private *> (d_ptr );}/
Friend 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;
If you use these two Macros in the Class header file, you can use the function to directly obtain the actual types of the object class and handle class. In addition, youyuan is also declared, this eliminates the need to worry about data and handle access permissions.

To facilitate calling in the CPP file, the following two macros are directly declared:
# Define q_d (class) class # private * const d = d_func ()
# Define q_q (class) class * const q = q_func ()
Well, it is easy to use, but in the future, the local variables cannot be declared as D and Q.

Here, the d_func and q_func functions are very common functions, which can be understood as a data class and a QT interface class.

qobject * parent;
points to the qobject parent class
qobjectlist children;
This is a bold design. If the system generates 1000000 qobject instances (for large systems, this number can be easily reached.) Every qobject sub-class averages 100 (This number may be large ),
the overhead of these pointers is 1000000*100*4 = 400 m, which is terrible. If we have to make a choice between flexibility and running overhead, no doubt QT chose the former, and it is difficult for me to comment on its advantages and disadvantages,
we still pray that we will not have this problem if we pray for a higher and stronger hardware level and the hekhwei name we have gained through QT over the years.
In short, qt indeed saves the tree structure of all class instances in the memory

Uint iswidget: 1;
Uint pendtimer: 1;
Uint blocksig: 1;
Uint wasdeleted: 1;
Uint ownobjectname: 1;
Uint sendchildevents: 1;
Uint receivechildevents: 1;
Uint unused: 25;
The code is simple, mainly because of some mark bits. To save memory overhead, the bitfield syntax is used here, and 25 bits are reserved as unused, which will be reserved for future expansion.
# Ifdef qt3_support
Int postedchildinsertedevents;
# Else
Int reserved;
# Endif
This may be to ensure compatibility with the serialized data in qt3. Even if no qt3_support is defined, a reserved data is retained to ensure the size of the entire qobjectdata remains unchanged.

Let's look at an example to learn more about the handle entity mode. This is the qpushbutton button class in QT.
The derived relationship of the qpushbutton handle class is:
Qobject
Qwidget
Qiniactbutton
Qpushbutton
The object class derivative relationship of qpushbutton is:
Qobjectdata
Qobjectprivate
Qwidgetprivate
Qiniactbuttonprivate
Qpushbuttonprivate

It can be seen that this is indeed a parallel system, but there is a qobjectprivate In the derived relationship of the entity class. This class encapsulates specific implementations such as thread processing, signal and slot mechanisms, it can be said that it is in the QT entity class.
The base class that actually works, and qobjectdata is just a simple data encapsulation.

First, I am not busy understanding the interfaces and implementations in the qobjectprivate class. Let's take a look at how the two systems of the handle class and entity class are constructed in QT?
Qpushbutton * Quit = new qpushbutton ("quit ");
Create a QT button, which is a simple line of code.
Qpushbutton: qpushbutton (const qstring & text, qwidget * parent)
: Qiniactbutton (* New qpushbuttonprivate, parent)
First, the qpushbutton constructor calls the qpusactbutton constructor. At the same time, a new qpushbuttonprivate entity class will be created, and the pointer will be converted to a reference and passed to qiniactbutton.

Qiniactbutton: qiniactbutton (qiniactbuttonprivate & DD, qwidget * parent)
: Qwidget (DD, parent, 0)
The constructor of qiniactbutton continues to call the constructor of the base class qwidget, and transmits the qpushbuttonprivate object class pointer to the base class.

Qwidget: qwidget (qwidgetprivate & DD, qwidget * parent, QT: wflags F)
: Qobject (DD, (parent & (parent-> windowtype () = QT: desktop ))? 0: parent), qpaintdevice ()
Qwidget continues sitting on the same thing

Qobject: qobject (qobjectprivate & DD, qobject * parent)
: D_ptr (& DD)
Finally, the base class qobject is used. Here, the qpushbuttonprivate pointer is assigned to d_ptr (remember this variable name)

The new qpushbuttonprivate generated during the qpushbutton construction is written to d_ptr in qobject.

Qobject: qobject (qobjectprivate & DD, qobject * parent)
: D_ptr (& DD)
{
Q_d (qobject );
: Qt_addobject (d_ptr-> q_ptr = This );
Qthread * currentthread = qthread: currentthread ();
D-> thread = currentthread? Qthreaddata: Get (currentthread)-> ID:-1;
Q_assert_x (! Parent | parent-> d_func ()-> thread = D-> thread, "qobject: qobject ()",
"Cannot create children for a parent that is in a different thread .");
If (parent & parent-> d_func ()-> thread! = D-> thread)
Parent = 0;
If (D-> iswidget ){
If (parent ){
D-> parent = parent;
D-> parent-> d_func ()-> children. append (this );
}
// No events sent here, this is done at the end of the qwidget Constructor
} Else {
Setparent (parent );
}
}
Then execute the constructor of qobject. Here, we mainly handle some threads and ignore it first.

Qwidget: qwidget (qwidgetprivate & DD, qwidget * parent, QT: wflags F)
: Qobject (DD, (parent & (parent-> windowtype () = QT: desktop ))? 0: parent), qpaintdevice ()
{
D_func ()-> Init (parent & parent-> windowtype () = QT: desktop? Parent: 0), F );
}
Then there is the constructor of qwidget. Here we call the init function of qwidgetprivate in the data class. This function is not a virtual function. Therefore, it is statically resolved to the init function call of qwidgetprivate.

Qiniactbutton: qiniactbutton (qiniactbuttonprivate & DD, qwidget * parent)
: Qwidget (DD, parent, 0)
{
Q_d (qiniactbutton );
D-> Init ();
}
Then there is the constructor of qiniactbutton. Here, the init function of qiniactbutton is called. This function is not a virtual function. Therefore, it is statically resolved to the init function call of qiniactbutton.

Qpushbutton: qpushbutton (const qstring & text, qwidget * parent)
: Qiniactbutton (* New qpushbuttonprivate, parent)
{
Q_d (qpushbutton );
D-> Init ();
Settext (text );
}
Then there is the qpushbutton constructor. The init function of qpushbutton is called here. This function is not a virtual function, so it is statically parsed to the qpushbutton init function call.

Now we have a clear idea. To sum up:
Qpushbutton generates the qpushbuttonprivate pointer at the same time during construction, and qpushbuttonprivate calls the constructors of the Data class base class in sequence during creation.
The constructor of qpushbutton shows that it calls the constructor of the base class and passes the qpushbuttonprivate pointer. When qpushbutton is created, it calls the constructor of the base class of the Interface Class in sequence.
The init function of the parallel data class is called in the constructor of the interface class. Because this function is not a virtual function, the init function of the Data class is called this time.

It should be noted that why should the qpushbuttonprivate object class pointer be converted to a reference? Why not directly pass pointers? The conclusion is that people like to write like this, that is, do not pass the pointer to the reference, and use a strange syntax like * New,
There is no way. Actually, the pointer is the same here, and the code looks more natural.

Delete quit;
After the structure is finished, let's talk about the structure.

Qpushbutton ::~ Qpushbutton ()
{
}
Of course, qpushbutton's destructor will be called here.

Qiniactbutton ::~ Qiniactbutton ()
{
# Ifndef qt_no_buttongroup
Q_d (qiniactbutton );
If (D-> group)
D-> group-> removebutton (this );
# Endif
}
Then the destructor of qiniactbutton

Qwidget ::~ Qwidget ()
{
Q_d (qwidget );
...
}
Then there is the destructor of qwidget. Here there is a lot of code, no matter it

Qobject ::~ Qobject ()
{
...
}
The last is the qobject destructor, which is also a lot
Q_d (qobject );
If (D-> wasdeleted ){
# If defined (qt_debug)
Qwarning ("Double qobject deletion detected ");
# Endif
Return;
}
D-> wasdeleted = true;
There is nothing to say about it. It is to set a wasdeleted mark to prevent being referenced again. It will be deleted immediately in the case of a single thread. What other mark is useless at all, however, this tag should be useful for multithreading.

// Set all qpointers for this object to zero
Guardhash * hash =: guardhash ();
If (hash ){
Qwritelocker locker (guardhashlock ());
Guardhash: iterator it = hash-> Find (this );
Const guardhash: iterator end = hash-> end ();
While (it. Key () = This & it! = END ){
* It. Value () = 0;
It = hash-> erase (it );
}
}
Here is the implementation code that supports qpointers. Let's talk about it later.

Emit destroyed (this );
When a pointer of QT is deleted, the destroyed signal must be sent. In general, there is no slot to respond.

qconnectionlist * List =: connectionlist ();
If (list) {
qwritelocker locker (& list-> lock );
list-> remove (this);
}< br> records in the signal slot mechanism are cleared.

If (D-> pendtimer ){
// Have pending timers
Qthread * thr = thread ();
If (THR | D-> thread = 0 ){
// Don't unregister timers in the wrong thread
Qiniacteventdispatcher * eventdispatcher = qiniacteventdispatcher: instance (THR );
If (eventdispatcher)
Eventdispatcher-> unregistertimers (this );
}
}
Here, the timer is cleared.

D-> eventfilters. Clear ();
Here, the event filtering mechanism is cleared.

// Delete children objects
If (! D-> children. isempty ()){
Qdeleteall (D-> children );
D-> children. Clear ();
}
Here, all subclass pointers are cleared. Of course, when each subclass pointer is cleared, all its subclasses are cleared. Therefore, the new pointer in QT rarely displays the corresponding Delete, as long as the top pointer is deleted by the framework,
All its associated subclasses are automatically deleted.

{
Qwritelocker locker (qobjectprivate: readwritelock ());
: Qt_removeobject (this );

/*
Theoretically, we cannot check D-> postedevents
Holding the posteventlist. mutex for the object's thread,
But since we hold the qobjectprivate: readwritelock (),
Nothing can go into qcoreapplication: postevent (), which
Effectively means noone can post new events, which is what
We are trying to prevent. This means we can safely check
D-> postedevents, since we are fairly sure it will not
Change (it cocould, but only by decreasing, I. e. Removing
Posted events from a differebnt thread)
*/
If (D-> postedevents> 0)
Qcoreapplication: removepostedevents (this );
}

If (D-> parent) // remove it from parent object
D-> setparent_helper (0 );

Delete D;
D_ptr = 0;
Here we need to delete the related data class pointer.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.