Qt internal: Object Data Storage

Source: Internet
Author: User

Statement:

  • The technology environment described in this article: QT 4.4.0 open source edition on Linux
  • This
    All the code in this article is just an exemplary code, just to illustrate the technology itself, so only the most basic code snippets are selected, and discard other redundant code that is not conducive to understanding the technology itself. Same
    These code snippets also omit some routine security checks, integrity checks, and other necessary work. Usually there is no practical value and it cannot be compiled directly.
  • This article is the original kudev.net Article, if you need to reference, reprint, please indicate the link to the http://www.kudev.net/bbs/thread-2448-1-1.html
    Thank you.

Object Data Storage, Background

We know that in C ++, almost every class requires some class member variables. The common practice is as follows:

Class personaldata
{
PRIVATE:
String mszname; // name
Bool mbsex; // gender
Int mnage; // age
};

When defining a class, you can directly define the class member variables here. Are you doing this?

In QT, this is hardly the case. To better understand how QT defines class member variables, let's first talk about the class member variable definition method in QT 2.x, because the method in 2.x is very easy to understand. Next, we will introduce the class member variable definition method in QT 4.4.

Methods In QT 2.x

When defining a class (. h file), only defines a Member Data Pointer, and then points to a data member object, this data member object contains all the member data of this class, then in the implementation file of the class (. (h file), define this private data member object. The sample code is as follows:

// File name: personaldata. h

Struct personaldatatevate; // declare the private data member type

Class personaldata
{
Public:
Personaldata (); // Constructor
Virtual ~ Personaldata (); // destructor

Void setage (const INT );
Int getage ();

PRIVATE:
Personaldatatevate * D;
};

// File name: personaldata. cpp

Struct personaldatatevate // defines the private data member type
{
String mszname; // name
Bool mbsex; // gender
Int mnage; // age
};

// Constructor
Personaldata: personaldata ()
{
D = new personaldatatevate;
};

// Destructor
Personaldata ::~ Personaldata ()
{
Delete D;
};

Void personaldata: setage (const int age)
{
If (age! = D-> mnage)
D-> mnage = age;
}

Int personaldata: getage ()
{
Return D-> mnage;
}

When I first learned QT, I also thought this method was very troublesome, but with the increase in usage, I began to like this method very much, and now I write the code, this method is basically used. Specifically, it has the following advantages:

1. reduce the dependency on header files.
 
Put all the specific data members in the CPP file,
In this way, when you need to modify data members, you only need to modify the CPP file instead of the header file, this avoids the re-compilation of all files containing this file due to the modification of the header file.
One time, especially when this header file is a very low-level header file and the project is very large, the advantage is obvious.
At the same time, this header file also reduces the dependency on other header files. You can include only the data members that need to be used in the CPP file once, instead of the header file to include

2. Enhanced class Encapsulation
This method enhances the encapsulation of classes, and cannot directly access class member variables. Instead, you must write the corresponding get/set member functions to do these tasks.
Off
In this case, the benevolent and wise have different opinions. Some people like to define all class member variables as public, which is convenient to use. I personally don't like this.
When a project becomes very large and many people are working on this project together, many people need to use the code they write at the underlying layer (# include) disadvantages of this method
It is fully reflected.

In addition, I do not like to define the variable names of data members as only one letter in QT 2.x. It looks very unintuitive, especially when searching. However, QT kernel does.

Methods In QT 4.4


In QT 4.4, the starting point of the class member variable definition method has not changed. However, the implementation method has changed dramatically.

In QT 4.4, a lot of macros are used to do things, which makes it more difficult to understand QT code. Macro is also widely used to define data variables of class members.

In this version, the class member variable no longer defines a private member for every class, but instead places this common job in the most basic base class qobject, then we defined some related methods for access. Well, let's go to the specific code.

// File name: qobject. h

Class qobjectdata
{
Public:
Virtual ~ Qobjectdata () = 0;
// Omitted
};

Class qobject
{
Q_declare_private (qobject)

Public:
Qobject (qobject * parent = 0 );

Protected:
Qobject (qobjectprivate & DD, qobject * parent = 0 );

Qobjectdata * d_ptr;
}

 
These codes are in the header file qobject. h. In qobject class
In the definition, we can see that the definition of the Data clerk is: qobjectdata * d_ptr; defined as protected
The type is to allow all the derived classes to access this variable, but it cannot be directly accessed externally.

The definition of qobjectdata is put in this header file, so that all member variables of the class inherited from qobject must be inherited from the class qobjectdata. The pure virtual destructor determine two things:

1. This class cannot be instantiated directly. In other words, the new qobjectdata line of code will certainly fail, compile cannot pass.
2. When the delete pointer variable is directed to any object inherited from qobjectdata, this object can be correctly deleted without generating errors, such, memory leakage.

Let's take a look at what this macro has done. q_declare_private (qobject)
# 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;

This
Macros mainly define two overloaded functions, d_func (). The function is to safely convert the data member variable d_ptr defined in the qobject class into a specific
Class data member type pointer. Let's take a look at the situation after the macro is expanded in the qobject class.

After q_declare_private (qobject) is expanded, the following code is used:
Inline qobjectprivate * d_func () {return reinterpret_cast <qobjectprivate *> (d_ptr );}
Inline const qobjectprivate * d_func () const
{Return reinterpret_cast <const qobjectprivate *> (d_ptr );}/
Friend class qobjectprivate;

After the macro scale was launched, a new problem emerged. Where did qobjectprivate come from? In the qobject class, why don't we directly use qobjectdata for the data member variable type?

Do you remember what we said just now? The pure virtual destructor of the class 'qobjectdata' indicates that this class cannot be instantiated. Therefore, the actual type of the member variable of the qobject class. This is inherited from qobjectdata, And it is qobjectprivate!

The following is the definition of this class:
Class qobjectprivate: Public qobjectdata
{
Q_declare_public (qobject)

Public:
Qobjectprivate (INT version = qobjectprivateversion );
Virtual ~ Qobjectprivate ();
// Omitted
}

So how can we prove that qobjectprivate is the data member variable d_ptr type after qobject instantiation? Let's take a look at the implementation of the constructor of qobject.

// File name: qobject. cpp

Qobject: qobject (qobject * parent)
: D_ptr (New qobjectprivate)
{
//...........................
}

Qobject: qobject (qobjectprivate & DD, qobject * parent)
: D_ptr (& DD)
{
//.....................
}

What is the second constructor used?
From the first constructor, we can clearly see that the d_ptr pointer in qobject class points to a qobjectprivate
And the class qobjectprivate is inherited from qobjectdata.
Let's look at the second constructor.
From the definition of qobject class, we can see that the second constructor is defined as protected.
Type, which indicates that this constructor can only be used by the inherited class, but cannot be used to directly export jsonobject objects. That is to say, if you write the following statement,
Compilation will fail,
New qobject (* New qobjectprivate, null)
To make it clearer, we take the class qwidget as an example.
Qwidget is the base class of all UI controls in QT. It inherits from qobject directly,
Class qwidget: Public qobject, public qpaintdevice
{
Q_object
Q_declare_private (qwidget)
//.....................
}
Let's look at the code of the constructor of this class:
Qwidget: qwidget (qwidget * parent, QT: windowflags F)
: Qobject (* New qwidgetprivate, 0), qpaintdevice ()
{
D_func ()-> Init (parent, F );
}
It is very clear that it calls the constructor of the Protection Type of the base class qobject and uses * New qwidgetprivate
As the first parameter. That is to say, the d_ptr pointer in the base class (qobject) will point to an object of the qwidgetprivate type,
Let's look at the definition of the class qwidgetprivate:
Class qwidgetprivate: Public qobjectprivate
{
Q_declare_public (qwidget)
//.....................
}
Well, this will link everything together.
The only statement d_func ()-> Init (parent, f) in the qwidget Constructor)
We noticed the following sentence in the definition of Class:
Q_declare_private (qwidget)
We have mentioned this macro before. When we expand this macro, it is like this:
Inline qwidgetprivate * d_func () {return
Reinterpret_cast <qwidgetprivate *> (d_ptr );}
Inline const qwidgetprivate * d_func () const {return
Reinterpret_cast <const qwidgetprivate *> (d_ptr);} friend
Class qwidgetprivate;
It is clear that the d_ptr pointer defined in qobject is converted to a qwidgetprivate pointer.
Summary:
To understand QT
The kernel code requires you to know how the data in each object in QT is saved, while QT does not define all the variables directly in the class as we usually write code.
So we cannot understand the corresponding class if we do not understand this problem.
In fact, the method for saving the class member data in qt4.4 is essentially the same as that in qt2.x, that is, defining a Member Data Pointer in class and pointing to the member data collection object (here
Is a qobjectdata or its derived class ). The method for initializing this member variable is to define a constructor of the protection type, and then in the constructor new of the derived class
A data member of a derived class, and assigns this new object to the Data Pointer of qobject. In use, you can define an inline function in a macro to put data pointers in a security class.
Type conversion, you can use it.

Private implementation

Introduction

Private implementation
(Aka. "pimpl") is a C ++ programming idiom which can be used to reduce
Compilation times, to enhance encapsulation and to retain binary
Compatibility.

Pimpl built in QT

Anyone who has ever peeked under the covers and looked into the sources of QT has most likely noticed macros calledQ_d
AndQ_q
. AlongQ_declare_private
AndQ_declare_public
, These macros provide qt's own pimpl mechanic.

Qobject
,
The heart of the QT object model, has a pointer to the private
Implementation in it's interface. The private implementation pointer,
"D_ptr" cannot be set to anything custom as it wocould prevent qobject
From reaching it's private implementation which it strictly relies on.

Each and every private implementation class in QT inheritsQobjectprivate
. For example qwidget
HasQwidgetprivate
And qpushbutton
HasQpushbuttonprivate
.
Where some of these private implementations are declared in separate
Headers, others are not. This makes it, in most cases, impossible
Use aforementioned Macros in one's own projects.

For example, let's say I'm subclassing qpushbutton
. I wowould have to subclass the private implementation fromQpushbuttonprivate
To avoid destroying qpushbutton
'S private implementation.Qpushbuttonprivate
Is declared inQpushbutton. cpp
So it's basically impossible to subclass.

In addition to this, QT's private headers/implementations are a subject to change. As it says in all the private headers:

//  W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.

In conclusion, the pimpl built in QT is of no use for one's own
Projects. it's not meant to be used and in your cases it's even
Impossible.

How to build your own pimpl mechanic

The Internet is full of various pimpl examples. To put it in
Nutshell, you simply provide a pointer to the private implementation in
The private interface of a class. Instead of storing Private Members
Directly to the class, place them into the private implementation
Class.

One can save himself or herself from building redundant
Mechanic from scratch to several classes by following the example
The pimpl Technique Used in QT. This can slightly reduce the amount
Handwork while declaring private implementation classes and later when
Referencing them. The following example
Makes use of the pimpl macros defined in QT with only some minor modifications.

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.