Qt父子对象内存管理实现简析

用Qt大家都知道Qt有一套自己的内存管理机制:父子对象管理机制。适用于所有继承于QObject的类。即在一个类中指定他的父对象,可以用void QObject::setParent(QObject * parent)函数指定,也可以在构造时传入参数,一般继承自QObject的类,Qt-c自动生成的类模板的构造函数都是explicit 类名(QObject *parent = 0);这个parent就是你可以制定的父对象。当一个对象删除之后,会自动删除他的所有的子对象,这样你就可以有很多new,但是只有一个delete。(注:这个父子对象关系和继承的父子类是不一样的,而且可以说基本没关系的。)

下面我们就扒下Qt的源码,找下它的实现。

主要就是集中在QObject类的实现。其中出现的地方就是构造函数,析构函数setParent函数QObjectPrivate类的setParent_helperdeleteChildren函数。

其中QObject和QObjectPrivate的关系,也是Qt中d-pointer的应用,关于d-pointer的内容,见链接:http://qt-project.org/wiki/Dpointer_SimplifiedChinese 和 http://www.cnblogs.com/SkylineSoft/articles/2046391.html

首先先说一个类成员结构,来保存标志位和子对象列表,和父对象指针。

01 class Q_CORE_EXPORT QObjectData {
02 public:
03    virtual ~QObjectData() = 0;
04    QObject *q_ptr;
05    QObject *parent;
06    QObjectList children;
07  
08    uint isWidget : 1;
09    uint blockSig : 1;
10    uint wasDeleted : 1;
11    uint isDeletingChildren : 1;
12    uint sendChildEvents : 1;
13    uint receiveChildEvents : 1;
14    uint isWindow : 1; //for QWindow
15    uint unused : 25;
16    int postedEvents;
17    QDynamicMetaObjectData *metaObject;
18    QMetaObject *dynamicMetaObject() const;
19 };

其中:

QObjectPrivate继承自QObjectData。

QObjectList 是:typedef QList<QObject*> QObjectList;

类似:uint  wasDeleted : 1; 这个冒号(:)的作用是指定这个unsigned  int内存对齐方式。

接着我们先看写QObject构造函数中的相关代码:

01 if (parent) {
02        QT_TRY {
03            if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData))
04                parent = 0;
05            setParent(parent);
06        } QT_CATCH(...) {
07            d->threadData->deref();
08            QT_RETHROW;
09        }
10    }

其中涉及到几个宏先说明下:

1 #  define QT_TRY try
2  
3 #  define QT_CATCH(A) catch (A)
4  
5 #  define QT_THROW(A) throw A

还有parent->d_func()是d-pointer的一个应用,返回的是QObjectPrivate * 。check_parent_thread函数是检查设置的父对象和当前对象是否是在一个线程,如果不是就返回false,这样就取消设置父对象,parent = 0;也就是把父对象设置为空。

parent ? parent->d_func()->threadData : 0  是一个三目运算,只有你设置了parent才会取出来他的线程信息,否则传进去是0,也就是空。

d->threadData :这还是d-pointer,d也就是当前QObject对应的QObjectPrivate对象的指针。

check_parent_thread的实现很简单,

如下就是简单的判断而已:

01 static bool check_parent_thread(QObject *parent,
02                                QThreadData *parentThreadData,
03                                QThreadData *currentThreadData)
04 {
05    if (parent && parentThreadData != currentThreadData) {
06        QThread *parentThread = parentThreadData->thread;
07        QThread *currentThread = currentThreadData->thread;
08        qWarning("QObject: Cannot create children for a parent that is in a different thread.n"
09                 "(Parent is %s(%p), parent's thread is %s(%p), current thread is %s(%p)",
10                 parent->metaObject()->className(),
11                 parent,
12                 parentThread ? parentThread->metaObject()->className() : "QThread",
13                 parentThread,
14                 currentThread ? currentThread->metaObject()->className() : "QThread",
15                 currentThread);
16        return false;
17    }
18    return true;
19 }

接着就是调用setParent函数了,我们就接着说setParent函数。

setParent函数的实现十分简单,只有三句话:

1 void QObject::setParent(QObject *parent)
2 {
3    Q_D(QObject);
4    Q_ASSERT(!d->isWidget);
5    d->setParent_helper(parent);
6 }

Q_D(QObject); 这是d-pointer,利用Q_D宏声明和定义QObjectPrivate * d.

Q_ASSERT(!d->isWidget); 断言d不是QWidgetPrivate 或者 其子类,也就是当前类不是QWidget或者其子类,QWidget重写了setParent函数,只支持QWidget设置父子对象的。

d->setParent_helper(parent);调用QObjectPrivate的setParent_helper函数。

接着说QObjectPrivate的setParent_helper函数。

01 void QObjectPrivate::setParent_helper(QObject *o)
02 {
03    Q_Q(QObject);
04    if (o == parent)
05        return;
06    if (parent) {
07        QObjectPrivate *parentD = parent->d_func();
08        if (parentD->isDeletingChildren && wasDeleted
09            && parentD->currentChildBeingDeleted == q) {
10            // don't do anything since QObjectPrivate::deleteChildren() already
11            // cleared our entry in parentD->children.
12        else {
13            const int index = parentD->children.indexOf(q);
14            if (parentD->isDeletingChildren) {
15                parentD->children[index] = 0;
16            else {
17                parentD->children.removeAt(index);
18                if (sendChildEvents && parentD->receiveChildEvents) {
19                    QChildEvent e(QEvent::ChildRemoved, q);
20                    QCoreApplication::sendEvent(parent, &e);
21                }
22            }
23        }
24    }
25    parent = o;
26    if (parent) {
27        // object hierarchies are constrained to a single thread
28        if (threadData != parent->d_func()->threadData) {
29            qWarning("QObject::setParent: Cannot set parent, new parent is in a different thread");
30            parent = 0;
31            return;
32        }
33        parent->d_func()->children.append(q);
34        if(sendChildEvents && parent->d_func()->receiveChildEvents) {
35            if (!isWidget) {
36                QChildEvent e(QEvent::ChildAdded, q);
37                QCoreApplication::sendEvent(parent, &e);
38            }
39        }
40    }
41    if (!wasDeleted && !isDeletingChildren && declarativeData && QAbstractDeclarativeData::parentChanged)
42        QAbstractDeclarativeData::parentChanged(declarativeData, q, o);
43 }

Q_Q(QObject);开始是去出来QObject * q指针。

接着判断当前对象的夫对象和你要设置的父对象是否一样,一样的话直接返回。

如果设置的夫对象和当前不一致,而且设置的父对象有值,就在父对象中移除当前子对象。

if (parentD->isDeletingChildren && wasDeleted && parentD->currentChildBeingDeleted == q) :这是判断是不是父对象在析构而且在删除当前对象而设置的。如果是则不做任何事情。parentD->isDeletingChildrenparentD->currentChildBeingDeleted是在QObjectPrivate::deleteChildren()设置的,wasDeleted这是系够函数~QObject设置的。

如果不是,还要判断,如果夫对象正在析构的话,也就直接设置副对象此子对象无效,否则就从父对象中删除此子对象。

接着把父对象设置为所要设置的对象。

然后为父对象添加此子对象,添加钱同样判断下父对象和子对象是否是在同一个线程,不再一个线程的话就取消设置,输出警告信息。

如果是在同一个线程,就为夫对象追加此子对象: parent->d_func()->children.append(q);

 

Next:我们就说下析构时的实现。

01 QObject::~QObject()
02 {
03    Q_D(QObject);//设置d
04    d->wasDeleted = true;
05 ······
06 if (!d->children.isEmpty())
07        d->deleteChildren();
08  
09    qt_removeObject(this);
10  
11    if (d->parent)        // remove it from parent object
12        d->setParent_helper(0);
13 }

其中,中间很多是处理信号槽的逻辑,就直接省略了,找出我们主题相关的代码。

   d->wasDeleted = true; 设置是否是析构的标志位。

if (!d->children.isEmpty()) d->deleteChildren(); 如果有子对象的话就调用deleteChildren函数。

 d->setParent_helper(0);设置父对象为0。也就是从夫父对象中删除次子对象。

QObjectPrivate::deleteChildren函数:

01 void QObjectPrivate::deleteChildren()
02 {
03    Q_ASSERT_X(!isDeletingChildren, "QObjectPrivate::deleteChildren()""isDeletingChildren already set, did this function recurse?");
04    isDeletingChildren = true;
05    // delete children objects
06    // don't use qDeleteAll as the destructor of the child might
07    // delete siblings
08    for (int i = 0; i < children.count(); ++i) {
09        currentChildBeingDeleted = children.at(i);
10        children[i] = 0;
11        delete currentChildBeingDeleted;
12    }
13    children.clear();
14    currentChildBeingDeleted = 0;
15    isDeletingChildren = false;
16 }

第一句,先断言已经开始再删除对象了,不是在删除,才能执行。

设置标志位,开始循环删除,也就是调用每个子对象的析构函数。

currentChildBeingDeleted是一个内部成员变量。

 

到此,对于这个实现,你应该对这个实现很熟悉了吧?这个设计很好的,了解了,以后自己做项目也可以自己构建一个父子对象内存管理机制的。

http://www.dushibaiyu.com/2014/07/qt-fuzi-neicun.html

展开阅读全文

没有更多推荐了,返回首页