25.11.10

The Beauty of Qt 1: D-Pointer / Private Implementation

I believe many of you who just start reading the source code of Qt will have the question: why Qt uses those private classes? What's the benefit of such a design pattern? Well, the most significant reason is to keep the binary compatibility.

Then what is binary compatibility? The article from KDE TechBase defines it like this:
A library is binary compatible, if a program linked dynamically to a former version of the library continues running with newer versions of the library without the need to recompile.
With this definition in mind, the benefit is easy to see: without it, whenever a library is updated, all the applications rely on it should be recompiled to work. Definitely, it's totally unacceptable for any widely used libraries like Qt. More information on binary compatibility itself can be found on that article from KDE TechBase, and here I only want to share with you how it's used.

Without the usage of D-pointers, we may have a class defined as below:
class MyClass
{
public:
MyClass();
~MyClass();
private:
int myVar;
};


Obviously, the private member of myVar here is a big enemy of binary compatibility. There fore, we refactor the code into the following:
class MyClassPrivate;
class MyClass
{
public:
MyClass();
~MyClass();
private:
MyClassPrivate * const d_ptr;
Q_DECLARE_PRIVATE(MyClass);
};


Here, we use a pointer d_ptr to refer to the private implementation, and the macro of Q_DECLARE_PRIVATE is employed to define some helper functions and friend class:
#define Q_DECLARE_PRIVATE(Class) \
inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \
inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } \
friend class Class##Private;


Then we define the private class as below:
class MyClassPrivate
{
public:
MyClassPrivate(MyClass *parent);
private:
MyClass * const q_ptr;
Q_DECLARE_PUBLIC(MyClass);
int myVar;
};


Here, the q_ptr pointer points the public interface, and the Q_DECLARE_PUBLIC macro is used to define some helper functions:
#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 Class;


And we can further use the following two macros to ease the access:
#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()


That's all about the usage of D-pointers and private implementations in Qt. Let me use a simple sample to end this post ;)
// myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H

#include <QtCore/QObject>

class MyClassPrivate;
class MyClass: public QObject
{
Q_OBJECT
public:
MyClass(QObject *parent = 0);
virtual ~MyClass();
void dummyFunc();

signal:
void dummySignal();

private:
MyClassPrivate * const d_ptr;
Q_DECLARE_PRIVATE(MyClass);
Q_DISABLE_COPY(MyClass);
};

#endif // MYCLASS_H

// myclass.cpp
#include "myclass.h"

class MyClassPrivate
{
public:
MyClassPrivate(MyClass *parent)
: q_ptr(parent)
{
}

void foobar()
{
Q_Q(MyClass);
emit q->dummySignal();
}

private:
MyClass * const q_ptr;
Q_DECLARE_PUBLIC(MyClass);
};

MyClass::MyClass(QObject *parent)
: QObject(parent)
, d_ptr(new MyClassPrivate(this))
{
}

MyClass::~MyClass()
{
Q_D(MyClass);
delete d;
}

void MyClass::dummyFunc()
{
Q_D(MyClass);
d->foobar();
}


The Chinese translation is available at: http://blog.csdn.net/zhu_xz/archive/2010/11/25/6035861.aspx

5 comments: