这个指针和 QSharedPointer

this pointer and QSharedPointer

我有一个类似 class 的树节点,名为 Message,它看起来像这样:

class Message
{
public:
    using Ptr = QSharedPointer<Message>;

public:
    explicit Message();
    explicit Message(Message::Ptr parentPtr);
    explicit Message(const Data &data, Message::Ptr parentPtr = Message::Ptr());

    void setParent(Message::Ptr parentPtr);
    Message::Ptr parent() const;
    bool hasParent() const;

    QSet<Message::Ptr> children() const;
    void setChildren(const QSet<Message::Ptr> &children);
    bool hasChildren() const;

    Data data() const;
    void setData(const Data &data);

private:
    void addChild(Message::Ptr childPtr);
    void removeChild(Message::Ptr childPtr);

private:
    Message::Ptr m_parentPtr;
    QSet<Message::Ptr> m_children;
    Data m_data;
};

这个 class 可以有一个父项和一组子项。我在实现 addChildsetParent 成员函数时遇到问题:

void Message::addChild(Message::Ptr childPtr)
{
    if (!m_children.contains(childPtr)) {
        m_children.insert(childPtr);
    }

    Message::Ptr thisPtr(this);

    if (childPtr->parent() != thisPtr) {
        childPtr->setParent(thisPtr);
    }
}

void Message::setParent(Message::Ptr parentPtr)
{
    if (m_parentPtr != parentPtr) {
        m_parentPtr = parentPtr;

        m_parentPtr->addChild(Message::Ptr(this));
    }
}

我预计会发生什么:

  1. Message::addChild 被调用
  2. thisPtr 创建时引用计数为 1
  3. childPtr->parent() != thisPtr 将解析为 true
  4. childPtr->setParent(thisPtr);Message::setParent 被执行并且 thisPtr 引用计数将随着创建共享指针的副本而增加 1。现在 thisPtr 的引用计数为 2
  5. Message::setParent 被执行时,m_parentPtr = parentPtr; 将增加 m_parentPtrparentPtr,因此 thisPtr 引用计数增加 1;这 3 个智能指针现在的引用计数为 3。
  6. 执行退出 Message::setParent 并销毁 parentPtrm_parentPtrthisPtr 的引用计数减少 1
  7. 执行 returns 到 Message::addChild。现在 thisPtr 的引用计数是 2.

实际发生了什么:

当执行退出 Message::addChild 中的 if 语句时 thisPtr 引用计数再次减 1,留下 thisPtr 的引用计数为 1。这使一切都崩溃了当执行存在时 Message::addChildthisPtr 被销毁,因此 this 被删除。

我的问题:

为什么 thisPtr 引用计数在执行退出 Message::addChild 中的 if 语句时再次减少,或者那里实际发生了什么?...

下面是它在调试器中的运行方式:

  1. As Message::setParent gets executed, m_parentPtr = parentPtr; will increase m_parentPtr, parentPtr and thus thisPtr reference counts by 1; these 3 smart pointers now have a reference count of 3.

5.1。然后,setParent 构造一个指向 child 的临时共享指针,引用计数为 1 并在 parent 上调用 addChild:

m_parentPtr->addChild(Message::Ptr(this));

5.2。 addChild 创建一个指向 parent 的共享指针,引用计数为 1:

Message::Ptr thisPtr(this);

5.3。 addChild returns,破坏了 5.2 的那个共享指针,它破坏了 parent,它破坏了 parent 的 QSet<Message::Ptr> m_children 成员。

5.4。 5.1的临时共享指针被销毁,即销毁child.

更一般地说,你有一个循环引用:parents拥有children,而children拥有他们的parents,这是记忆的秘诀泄漏和 use-after-delete 错误。构造新的共享指针拥有其他共享指针已经拥有的原始指针是 double-delete 和 use-after-delete 错误的秘诀;共享指针不会相互了解,它们的引用计数将独立变化。您应该调查 QWeakPointer to break the cycle and QEnableSharedFromThis 以安全地获取指向 *this.

的共享指针