Home > database >  Qt use of Class and CLassPrivate
Qt use of Class and CLassPrivate

Time:07-31

When examining classes written by Qt, I discover many contain a class called "Class", and another called "ClassPrivate". For example QFile contains QFile and QFilePrivate, QIODevice contains QIODevice and QIODevicePrivate.

What is the purpose of this design?

CodePudding user response:

In Qt 4.8, we see this structure please look at enter image description here

But in Qt6 we don't have QFilePrivate or ClassPrivate classes. look at enter image description here

What is the purpose of this design?

I guess they want to provide very strong encapsulationYou cannot access the d pointer as a user of the API, which is the whole point of encapsulation.

enter image description here

d_ptr is a pointer in QObject class in Qt 4.8

Look at D-Pointer document.

The trick is to keep the size of all public classes of a library constant by only storing a single pointer. This pointer points to a private/internal data structure that contains all the data. The size of this internal structure can shrink or grow without having any side-effect on the application because the pointer is accessed only in the library code and from the application's point of view the size of the object never changes - it's always the size of the pointer. This pointer is called the d-pointer.

The spirit of this pattern is outlined in the code below (all code in this article doesn't have destructors, of course you should add them in real code).

CodePudding user response:

This is a common design pattern called private implementation, or pimpl for short. Qt actually calls it d/q pointer, but it is the same thing.

The Q_D and Q_Q macros are part of a design pattern called the d-pointer (also called the opaque pointer) where the implementation details of a library may be hidden from its users and changes to the implementation can be made to a library without breaking binary compatibility.

The main reason for this in binary compatibility. Qt, unlike many other projects like boost, has had the guarantee of API (source) and ABI (binary) compatibility for the same major version.

So, the API and ABI would not break e.g. inside the Qt 5.x series. They would have to change the major version to break any of this.

Now, the private implementation really helps with this as the implementation details are no longer in the publicly available header file.

They can add further private members (method or variable) in the private implementation while the public interface would still online contain a private implementation pointer.

This makes the public interface completely opaque allowing flexibility in changing the implementation details without having to worry about the public interface.

Here is an example they give:

class Widget
{
   // ...
private:
    Rect m_geometry;
    String m_stylesheet; // NEW in WidgetLib 1.1
};
 
class Label : public Widget
{
public:
    // ...
    String text() const
    {
        return m_text;
    }
 
private:
    String m_text;
};

This would break the client code using Widget causing a crash. However, if you hide the implementation details of the Widget class behind a private pointer, you can do anything with it since it is hidden. So, in the above example, you can add "m_stylesheet" without any risks and the client code using Label will not crash.

In case you wonder why binary compatibility is such a big deal: Qt has been a popular framework used by many applications, some really big. It would be a major pain in the neck to upgrade the Qt dependency for them if it did not offer binary compatibility.

When designing libraries like Qt, it is desirable that applications that dynamically link to Qt continue to run without recompiling even after the Qt library is upgraded/replaced with another version. For example, if your application CuteApp was based on Qt 4.5, you should be able to upgrade the Qt libraries (on Windows shipped with the application, on Linux often comes from package manager automatically!) from version 4.5 to Qt 4.6 and your CuteApp that was built with Qt 4.5 should still be able to run.

Basically, when you upgrade Qt in this way for your application, you can just drop in the new version without even having to recompile your application. And the features of your application will just keep working as before. This is great because you do not need to send a new version of your application to the customers. They can just upgrade Qt on their side and benefit from performance improvements in Qt or bug fixes indeed, etc.

And as a matter of convention, these private implementation are written in Qt inside files with the _p.h suffix.

You can also refer to this page for further binary compatibility examples.

  • Related