Qt源码分析之QObject

  在分析源码之前,我们先来介绍下Pimpl机制。。。

Pimpl机制介绍

  Pimpl(private implementation) 字面意思是私有实现。具体实现是将类的(假设类A)私有数据和函数放入一个单独的类(假设类Pimpl)中,然后在类A的头文件中对该类Pimpl进行前置声明,接着在类A中声明一个私有的指向该Pimpl类的指针, 在类A的构造函数中分配类Pimpl,这样做的主要目的是解开类的使用接口和实现的耦合。

为什么要使用 Pimpl机制

1、更加符合c++的封装性

  封装有一个很重要的属性就是信息隐藏性,即利用接口机制隐蔽内部细节,达到将对象的使用者和设计者分开的目的,以提高软件的可维护性和可修改性。使用Pimpl机制,只要我们不修改公有接口,使用者永远看到的是同一个共有接口和一个指针。

2、节约编译时间

  一个大型c++工程的编译所消耗的时间往往很感人。如果不使用Pimpl机制,我们修改了某一个头文件,那么将会导致所有包含该头文件的源文件都会被重新编译,那么编译成本就会很高。如果使用了Pimpl机制,就不会这样。

例子:
简单的打印m_msg的类

printmsg.h

#ifndef PRINTMSG_H
#define PRINTMSG_H
#include <QString>

class PrintMsg
{
public:
    PrintMsg();
    void print();

private:
    QString m_msg;
};

#endif // PRINTMSG_H

  如果此时我们在printmsg.h 中添加一个m_msg1,所有包含printmsg.h的代码都会会被重新编译。

#ifndef PRINTMSG_H
#define PRINTMSG_H
#include <QString>

class PrintMsg
{
public:
    PrintMsg();

    void print();

private:
    QString m_msg;
    QString m_msg1;
};

#endif // PRINTMSG_H

如果使用Pimpl机制:
将具体实现放入PrintMsgPrivate类中:

printmsgprivate.h

#ifndef PRINTMSGPRIVATE_H
#define PRINTMSGPRIVATE_H
#include <QString>

class PrintMsgPrivate
{
public:
    PrintMsgPrivate();

    void printmsg();

private:
    QString m_msg;
};

#endif // PRINTMSGPRIVATE_H

PrintMsg类:

#ifndef PRINTMSG_H
#define PRINTMSG_H
#include <QString>
#include <QScopedPointer>

class PrintMsgPrivate;
class PrintMsg
{
public:
    PrintMsg();
    ~PrintMsg();

    void print();

private:
    QScopedPointer<PrintMsgPrivate> m_pPrint;
};

#endif // PRINTMSG_H

#include "printmsg.h"
#include "pritmsgprivate.h"

PrintMsg::PrintMsg()
    : m_pPrint(new PrintMsgPrivate())
{

}

void PrintMsg::print()
{
    m_pPrint->printmsg();
}

这里使用了Qt的智能指针类QScopedPointer,此时我们在PrintMsgPrivate中添加一个m_msg2, 再次编译只需要重新编译printmsgprivate.cpp 和 printmsg.cpp 即可。

Qt源码分析之QObject

原文: http://blog.csdn.net/oowgsoo/article/details/1529284

用sizeof() 看一下QObject的大小为8,除了虚函数表占4个字节,另外四个字节是:

class Q_CORE_EXPORT QObject
{
...
protected:
    QScopedPointer<QObjectData> d_ptr;
...
}

一个指向 QObjectData 的智能指针。这里就使用了Pimpl机制。来看一下QObjectData:

class Q_CORE_EXPORT QObjectData {
public:
    virtual ~QObjectData() = 0;
    QObject *q_ptr;
    QObject *parent;
    QObjectList children;

    uint isWidget : 1;
    uint blockSig : 1;
    uint wasDeleted : 1;
    uint isDeletingChildren : 1;
    uint sendChildEvents : 1;
    uint receiveChildEvents : 1;
    uint isWindow : 1; //for QWindow
    uint unused : 25;
    int postedEvents;
    QDynamicMetaObjectData *metaObject;
    QMetaObject *dynamicMetaObject() const;
};
}

  d_ptr与QObjectData中的q_ptr遥相呼应,使得接口类和实现类可以双向的引用。为什么是这样的命名方式呢?可能q指的是Qt接口类,d指的是Data数据类,这当然是猜测了,但是或许可以方便你记忆,在Qt中,这两个指针名字是非常重要的,必须记住,但是仅仅如此还是不容易使用这两个指针,因为它们都是基类的类型,难道每次使用都要类型转换吗?为了简单起见,Qt在这里声明了两个宏:

#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;
    ...

#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;

qGetPtrHelper:

template <typename T> static inline T *qGetPtrHelper(T *ptr)
{ 
    return ptr;  
}

  只要在类的头文件中使用这两个宏,就可以通过函数直接得到实体类和接口类的实际类型了,而且这里还声明了友元,使得数据类和接口类连访问权限也不用顾忌了。我们再看一下Class##Private这个类,QObject中即QObjectPrivate:

class Q_CORE_EXPORT QObjectPrivate : public QObjectData
{
    Q_DECLARE_PUBLIC(QObject)
    ...
};

通过查看源码发现,QObjectPrivate继承自QObjectData,而QObjectPrivate,这个类封装了线程处理,信号和槽机制等具体的实现,可以说它才是Qt实体类中真正起作用的基类,而QObjectData不过是一层浅浅的数据封装而已。

为了cpp文件中调用的方便,更是直接声明了以下两个宏:

#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()

QObject 中即:

#define Q_D(QObject) QObjectPrivate * const d = d_func()
#define Q_Q(QObject) QObject* const q = q_func()

好了,使用起来倒是方便了,但是以后局部变量可千万不能声明为d和q了。
这里的d_func和q_func函数是非常常用的函数,可以理解为一个是得到数据类,一个是得到Qt接口类。

再来看QObjectData:

QObject *parent;
QObjectList children; 

再来看QObject中:

class Q_CORE_EXPORT QObject
{
...

public:
    inline QObject *parent() const { return d_ptr->parent; }
...

inline const QObjectList &children() const { return d_ptr->children; }
};

  可以看出parent 指向了当前QObject的父类,children则保存了当前QObject的所有子类的指针。
  总之,Qt确实在内存中保存了所有类实例的树型结构。

来看一个具体的实例,QPushButton,

构造

QPushButton的接口类派生关系是:

QObject  
 QWidget  
  QAbstractButton  
   QPushButton

QPushButton的具体实现类派生关系是:

QObjectData  
 QObjectPrivate  
  QWidgetPrivate  
   QAbstractButtonPrivate  
    QPushButtonPrivate 

所有具体的实现都放在了Class##Private类中。
通过QPushButton的一个构造函数来看看它们是如何联系的:

QPushButton::QPushButton(QWidget *parent)
    : QAbstractButton(*new QPushButtonPrivate, parent)

QAbstractButton 的一个构造函数:
QAbstractButton(QAbstractButtonPrivate &dd, QWidget* parent)
    : QWidget(dd, parent, 0) 

QWidget的一个构造函数:
QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WFlags f)  
    : QObject(dd, ((parent && (parent->windowType() == Qt::Desktop)) ? 0 : parent)), QPaintDevice() 

QObject的一个构造函数:
QObject::QObject(QObjectPrivate &dd, QObject *parent)  
    : d_ptr(&dd) 

  首先QPushButton的构造函数中调用了QAbstractButton的构造函数,同时马上new出来一个QPushButtonPrivate实体类,然后把指针转换为引用传递给QAbstractButton,
QAbstractButton的构造函数中继续调用基类QWidget的构造函数,同时把QPushButtonPrivate实体类指针继续传给基类。

QWidget继续坐着同样的事情:

QObject::QObject(QObjectPrivate &dd, QObject *parent)  
    : d_ptr(&dd) 

终于到了基类QObject,这里就直接把QPushButtonPrivate的指针赋值给了d_ptr。最终在QPushButton构造时同时产生的new QPushButtonPrivate被写到了QObject中的d_ptr中。

QObject::QObject(QObjectPrivate &dd, QObject *parent)
    : d_ptr(&dd)
{
    Q_D(QObject);
    d_ptr->q_ptr = this;
    d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current();
    d->threadData->ref();
    if (parent) {
        QT_TRY {
            if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData))
                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);
            }
        } QT_CATCH(...) {
            d->threadData->deref();
            QT_RETHROW;
        }
    }
#if QT_VERSION < 0x60000
    qt_addObject(this);
#endif
    if (Q_UNLIKELY(qtHookData[QHooks::AddQObject]))
        reinterpret_cast<QHooks::AddQObjectCallback>(qtHookData[QHooks::AddQObject])(this);
}

然后执行QObject的构造函数,这里主要是一些线程的处理,先不理它

QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WindowFlags f)
    : QObject(dd, 0), QPaintDevice()
{
    Q_D(QWidget);
    QT_TRY {
        d->init(parent, f);
    } QT_CATCH(...) {
        QWidgetExceptionCleaner::cleanup(this, d_func());
        QT_RETHROW;
    }
}

然后是QWidget的构造函数,这里调用了数据类QWidgetPrivate的init函数,这个函数不是虚函数,因此静态解析成QWidgetPrivate的init函数调用。

QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent)  
    : QWidget(dd, parent, 0)  
{  
    Q_D(QAbstractButton);  
    d->init();  
} 

然后是QAbstractButton的构造函数,这里调用了数据类QAbstractButton的init函数,这个函数不是虚函数,因此静态解析成QAbstractButton的init函数调用。

QPushButton::QPushButton(QWidget *parent)
    : QAbstractButton(*new QPushButtonPrivate, parent)
{
    Q_D(QPushButton);
    d->init();
}

然后是QPushButton的构造函数,这里调用了数据类QPushButton的init函数,这个函数不是虚函数,因此静态解析成QPushButton的init函数调用。

总结一下:

  QPushButton在构造的时候同时生成了QPushButtonPrivate针,QPushButtonPrivate创建时依次调用数据类基类的构造函数。QPushButton的构造函数中显示的调用了基类的构造函数并把QPushButtonPrivate指针传递过去,QPushButton创建时依次调用接口类基类的构造函数。在接口类的构造函数中调用了平行数据类的init函数,因为这个函数不是虚函数,因此就就是此次调用了数据类的init函数。

析构

然后是析构:

QPushButton::~QPushButton()
{
}

QPuButton 析构函数。

QAbstractButton::~QAbstractButton()  
{  
#ifndef QT_NO_BUTTONGROUP  
    Q_D(QAbstractButton);  
    if (d->group)  
        d->group->removeButton(this);  
#endif  
} 

QAbstractButton 析构函数。

QWidget::~QWidget()
{
    Q_D(QWidget);
    ...
}

QWidget析构函数,一大堆。

QObject::~QObject()
{
    Q_D(QObject);
    d->wasDeleted = true;
    ...

    ...
}

QObject 析构函数。wasDeleted防止在多线程下被重复删除。

if (!d->children.isEmpty())
        d->deleteChildren();

  这里清除所有子类指针,当然每个子类指针清除时又会清除它的所有子类,因此Qt中new出来的指针很少有显示对应的delete,因为只要最上面的指针被框架删除了,它所连带的所有子类都被自动删除了。QObject 的析构函数还做了大量的其它的删除清理工作,大家自行研究。。。

猜你喜欢

转载自blog.csdn.net/a844651990/article/details/78801518