seo1888网站建设,苏州搜索引擎排名优化商家,网站收藏本站的功能,wordpress引用b站视频对象树管理 个人经验总结#xff0c;如有错误或遗漏#xff0c;欢迎各位大佬指正 #x1f603; 文章目录对象树管理设置父对象的作用设置父对象(setParent)完整源码片段分析对象的删除夹带私货时间设置父对象的作用
众所周知#xff0c;Qt中#xff0c;有为对象设置父对象…对象树管理 个人经验总结如有错误或遗漏欢迎各位大佬指正 文章目录对象树管理设置父对象的作用设置父对象(setParent)完整源码片段分析对象的删除夹带私货时间设置父对象的作用
众所周知Qt中有为对象设置父对象的方法——setParent。
而设置父对象的作用主要有在父对象析构的时候会自动去析构其子对象。如果是一个窗口对象如果其父对象设置了样式表(Style Sheet)子对象也会继承父对象的样式。
所以这篇文章咱们主要看一下setParent的源码以及QObject是怎么进行对象管理的。
设置父对象(setParent)
我们可以看到setParent这个函数就是调用了QObjectPrivate类的setParent_helper这个函数。
void QObject::setParent(QObject *parent)
{Q_D(QObject);Q_ASSERT(!d-isWidget);d-setParent_helper(parent);
}所以我们进一步分析setParent_helper这个函数
完整源码
void QObjectPrivate::setParent_helper(QObject *o)
{Q_Q(QObject);// 不能把自己设为父对象Q_ASSERT_X(q ! o, Q_FUNC_INFO, Cannot parent a QObject to itself);
#ifdef QT_DEBUG// 检查对象树的循环const auto checkForParentChildLoops qScopeGuard([](){int depth 0;auto p parent;while (p) {if (depth CheckForParentChildLoopsWarnDepth) {qWarning(QObject %p (class: %s, object name: %s) may have a loop in its parent-child chain; this is undefined behavior,q, q-metaObject()-className(), qPrintable(q-objectName()));}p p-parent();}});
#endif// 如果要设置的父对象就是当前的父对象直接返回if (o parent)return;if (parent) {QObjectPrivate *parentD parent-d_func();if (parentD-isDeletingChildren wasDeleted parentD-currentChildBeingDeleted q) {// dont do anything since QObjectPrivate::deleteChildren() already// cleared our entry in parentD-children.} else {const int index parentD-children.indexOf(q);if (index 0) {// were probably recursing into setParent() from a ChildRemoved event, dont do anything} else 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 threadif (threadData ! parent-d_func()-threadData) {qWarning(QObject::setParent: Cannot set parent, new parent is in a different thread);parent nullptr;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);
}片段分析 一些先决条件的判断 判断设置的父对象是否是自己 // 不能把自己设为父对象 Q_ASSERT_X(q ! o, Q_FUNC_INFO, Cannot parent a QObject to itself); /*...*/ // 如果要设置的父对象就是当前的父对象直接返回 if (o parent) return;判断原来的父对象是否处于正在删除子对象的过程中并且当前对象已经被删除了如果是则什么都不做有点迷惑 if (parentD-isDeletingChildren wasDeleted parentD-currentChildBeingDeleted q) {// dont do anything since QObjectPrivate::deleteChildren() //already cleared our entry in parentD-children.}判断是不是通过从ChildRemoved事件递归到setParent() if (index 0) { // were probably recursing into setParent() from a ChildRemoved event,// dont do anything
} else 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 threadif (threadData ! parent-d_func()-threadData) {qWarning(QObject::setParent: Cannot set parent, \new parent is in a different thread);parent nullptr;return;}// 父对象添加子对象并发送事件parent-d_func()-children.append(q);if(sendChildEvents parent-d_func()-receiveChildEvents) {if (!isWidget) {QChildEvent e(QEvent::ChildAdded, q);QCoreApplication::sendEvent(parent, e);}}}对象的删除 然后就是对象的管理也就是在父对象析构的时候自动析构掉所有的子对象。这一个在我们使用窗口部件的时候很有用因为一个界面可能有很多个子控件比如按钮、label等这时候如果一个小窗口被关闭我们也不需要一个一个的去析构由Qt的对象树去进行析构就好了。
QObject::~QObject()
{/*...*/// 删除子对象if (!d-children.isEmpty())d-deleteChildren();#if QT_VERSION 0x60000qt_removeObject(this);
#endifif (Q_UNLIKELY(qtHookData[QHooks::RemoveQObject]))reinterpret_castQHooks::RemoveQObjectCallback(qtHookData[QHooks::RemoveQObject])(this);Q_TRACE(QObject_dtor, this);if (d-parent) // remove it from parent objectd-setParent_helper(nullptr);
}将所有的子对象进行删除遍历容器按照子对象所加入进来的顺序进行析构。
void QObjectPrivate::deleteChildren()
{// 清空子对象Q_ASSERT_X(!isDeletingChildren, QObjectPrivate::deleteChildren(), isDeletingChildren already set, did this function recurse?);isDeletingChildren true;// delete children objects// dont use qDeleteAll as the destructor of the child might// delete siblingsfor (int i 0; i children.count(); i) {currentChildBeingDeleted children.at(i);children[i] 0;delete currentChildBeingDeleted;}children.clear();currentChildBeingDeleted nullptr;isDeletingChildren false;
}夹带私货时间
在使用Qt的对象树这个功能的时候可能会遇到一种问题会导致程序崩溃就是手动的管理(也就是直接delete)一个有父对象的QObject为什么会出现这样的情况呢因为你在delete子对象之后并没有把这个对象从父对象的对象树里移除。在父对象进行析构的时候还是会去遍历子对象容器一个一个析构。这个时候就会出现一个对象指针被删除了两次自然就会崩溃。
那么如果非要自己管理这个对象有什么办法呢我们从对象树下手有两种办法 使用deleteLater 就是调用QObject对象的deleteLater函数来实现删除。关于deleteLater的分析可以看这个大佬的文章Qt 中 deleteLater() 函数的使用 QObject *object new QObject();
QObject *m_child new QObject(object);// 需要手动删除的时候
m_child-deleteLater();先将父对象设置为空再直接delete QObject *object new QObject();
QObject *m_child new QObject(object);// 需要手动删除的时候
m_child-setParent(nullptr);
delete m_child;
m_child nullptr;先将父对象设置为空再直接delete QObject *object new QObject();
QObject *m_child new QObject(object);// 需要手动删除的时候
m_child-setParent(nullptr);
delete m_child;
m_child nullptr;个人建议使用第一种方法也就是调用deleteLater