qt 对象管理

组合设计模式

设计模式:可复用面向对象软件及基础:3-3 结构型模式:组合模式(composite)_~怎么回事啊~的博客-CSDN博客

QObject(组合模式):父对象&& 子对象

qt d指针和对象树_~怎么回事啊~的博客-CSDN博客

qt 对象管理_第1张图片

 

1 特性

不同层次解释

        从数据层看:组合模式/整体-部分模式

        QObject组合模式 从UI层看也可以称为:句柄-实体模式

1 QObject是唯一的句柄基类,d_ptr指向实体(数据)类

2 QObjectData 是唯一的实体(数据)基类,q_ptr指向实体对象对应的QObject(句柄类)

3 q_ptr(接口类/句柄基类)和d_ptr指针(实体/数据类)使得句柄和实体可以双向引用

4  q_ptr = q_func() 获取数据类 /  d_ptr = d_fun 获取接口类

            QObject 中有 QScopedPointer d_ptr;

qt 对象管理_第2张图片

        并且在构造函数中,创建了 QObjectPrivate ,并且让d_prt的q_ptr 指向自己,QObjectPrivate继承于QObjectData

qt 对象管理_第3张图片

         QObjectData是所有数据类的唯一基类,它里面存储了q_ptr,children(子对象列表)

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 deleteLaterCalled : 1;
    uint unused : 24;
    int postedEvents;
    QDynamicMetaObjectData *metaObject;
    QMetaObject *dynamicMetaObject() const;
};

基本特性

        任何继承自QObject的派生对象都可以看作是一个QObject对象,可以使用信号与槽与其他QObject对象通信

对象列表

        每一个QObject对象只能有一个父类QObject对象和任意数量的子类QObject对象,每个QObject对象都有一个QObjectList存放子对象列表

1 子对象列表的作用:为父类和子类建立了一个双向联系,父对象知道子对象的地址,子对象也知道父对象的地址,

对象管理

        每个父对象都会管理自己的子对象,在父对象析构时会自动释放其所关联的子对象

管理:

        1 获取子对象列表children()    

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

typedef QList QObjectList;  孩子对象用链表存储,方便频繁的删除和添加

        2 查找指定子对象列表 findChildren()

    template
    inline T findChild(const QString &aName = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
    {
        typedef typename std::remove_cv::type>::type ObjType;
        return static_cast(qt_qFindChild_helper(this, aName, ObjType::staticMetaObject, options));
    }

// 通过name 查找特定的孩子

QObject *qt_qFindChild_helper(const QObject *parent, const QString &name, const QMetaObject &mo, Qt::FindChildOptions options)
{
    if (!parent)
        return 0;
    const QObjectList &children = parent->children();
    QObject *obj;
    int i;
    for (i = 0; i < children.size(); ++i) {
        obj = children.at(i);
        if (mo.cast(obj) && (name.isNull() || obj->objectName() == name))
            return obj;
    }
    if (options & Qt::FindChildrenRecursively) {
        for (i = 0; i < children.size(); ++i) {
            obj = qt_qFindChild_helper(children.at(i), name, mo, options);
            if (obj)
                return obj;
        }
    }
    return 0;
}

   3  销毁孩子结点  通过析构函数:QObject析构函数

        销毁对象,删除其所有子对象。所有进出对象的信号都会自动断开,并且对象的任何未决发布事件都将从事件中删除队列。但是,使用 deleteLater() 通常比使用直接删除 QObject 子类更加安全。

QObject::~QObject()
{
    Q_D(QObject);
    d->wasDeleted = true;
    d->blockSig = 0; // unblock signals so we always emit destroyed()

......
    //  1 删除孩子结点
    if (!d->children.isEmpty())
        d->deleteChildren();

#if QT_VERSION < 0x60000
    qt_removeObject(this);
#endif
    if (Q_UNLIKELY(qtHookData[QHooks::RemoveQObject]))
        reinterpret_cast(qtHookData[QHooks::RemoveQObject])(this);

   // 将自己的从父结点中移除
    if (d->parent)        // remove it from parent object
        d->setParent_helper(0);
}


void QObjectPrivate::deleteChildren()
{
    Q_ASSERT_X(!isDeletingChildren, "QObjectPrivate::deleteChildren()", "isDeletingChildren already set, did this function recurse?");
    isDeletingChildren = true;
    // delete children objects
    // don't use qDeleteAll as the destructor of the child might
    // delete siblings

    //遍历孩子结点
    for (int i = 0; i < children.count(); ++i) {
        currentChildBeingDeleted = children.at(i);
        children[i] = 0;
        delete currentChildBeingDeleted;//删除孩子结点指针 为了调用孩子结点的析构函数,从而递归删除孩子结点
    }
    children.clear();
    currentChildBeingDeleted = 0;
    isDeletingChildren = false;
}

重设父对象

        重新设定子对象的父对象

要求:子对象和父对象必须在同线程

自定义类qttest 在构造函数中

qt 对象管理_第4张图片

调用QWiget的构造:

qt 对象管理_第5张图片 

因为QWidget 继承QObject ,因此调用 QObject的构造函数:因此,在qttest类中,最终d_ptr指向QWidgetPrivate类

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(qtHookData[QHooks::AddQObject])(this);
}

使用:void QObject::setParent(QObject *parent)

/*!
    Makes the object a child of \a parent.

    \sa parent(), children()
*/
void QObject::setParent(QObject *parent)
{
    Q_D(QObject);
    Q_ASSERT(!d->isWidget);
    d->setParent_helper(parent);
}

void QObjectPrivate::setParent_helper(QObject *o)
{
    Q_Q(QObject);
    if (o == parent)
        return;
    if (parent) {
        QObjectPrivate *parentD = parent->d_func();
        if (parentD->isDeletingChildren && wasDeleted
            && parentD->currentChildBeingDeleted == q) {
            // don't do anything since QObjectPrivate::deleteChildren() already
            // cleared our entry in parentD->children.
        } else {
           //从之前的父对象的孩子列表删除该对象
            const int index = parentD->children.indexOf(q);
            if (parentD->isDeletingChildren) {
                parentD->children[index] = 0;
            } else {
                parentD->children.removeAt(index);
                if (sendChildEvents && parentD->receiveChildEvents) {
                    QChildEvent e(QEvent::ChildRemoved, q);
                    QCoreApplication::sendEvent(parent, &e);
                }
            }
        }
    }
    parent = o;
    if (parent) {
        // object hierarchies are constrained to a single thread
        if (threadData != parent->d_func()->threadData) {
            qWarning("QObject::setParent: Cannot set parent, new parent is in a different thread");
            parent = 0;
            return;
        }
        //将新的父类孩子列表中添加该对象
        parent->d_func()->children.append(q);
        if(sendChildEvents && parent->d_func()->receiveChildEvents) {
            if (!isWidget) {
                QChildEvent e(QEvent::ChildAdded, q);
                QCoreApplication::sendEvent(parent, &e);
            }
        }
    }
    if (!wasDeleted && !isDeletingChildren && declarativeData && QAbstractDeclarativeData::parentChanged)
        QAbstractDeclarativeData::parentChanged(declarativeData, q, o);
}

创建方式

       没有父对象的在栈区创建,位于栈区对象在超出作用域时被销毁,子对象也随之被销毁

        有父对象的在堆区创建

qt 对象管理_第6张图片

当我们创建了一个对象,并且指定父对象时,内部做了哪些处理呢?

调用QPushButton的构造函数,

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

 调用QAbstractButton的构造函数

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

调用QWidget的构造函数

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

调用QObject的构造函数,将d_ptr赋值dd,d向上追溯是QPushButtonPrivate;但是由于传递的是QObject(dd,0);也就是parent在QObject的构造函数中是空的,因此不会走到下面的if (parent)中

/*!
    \internal
 */
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(qtHookData[QHooks::AddQObject])(this);
}

真正设置父子关系的地方在:

qt 对象管理_第7张图片

 继续看 void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) 这个函数:

void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f)
{
    ...
    else if (parentWidget)
        q->setParent(parentWidget, data.window_flags);
	...
}

最终调用到 QObjectPrivate::setParent_helper(QObject *o) 这个函数完成将该对象添加到父对象的孩子链表中;

复合对象和组合对象

        复合对象:复合对象通常将两个或多个现有对象组合成单个对象

        组合对象:组合对象是要将多个元素作为一个对象来处理,需要将它们组合

关系

        QObjectData 数据封装

        QObjectPriavte封装的是线程和具体信号的处理

总结:

qt 对象管理_第8张图片

 

 

你可能感兴趣的:(qt,c++)