2

我有一个类似树节点的类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;
};

这个类可以有一个父级和一组子级。我在实现addChildandsetParent成员函数时遇到了问题:

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并销毁将和parentPtr的引用计数减1m_parentPtrthisPtr
  7. 执行返回Message::addChild。现在的引用计数thisPtr为 2。

实际发生的情况:

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

我的问题:

为什么当执行退出语句或那里实际发生的情况thisPtr时引用计数再次减少?...ifMessage::addChild

以下是它在调试器中的运行方式: 在此处输入图像描述

4

1 回答 1

3
  1. 随着Message::setParent执行,m_parentPtr = parentPtr;将增加m_parentPtrparentPtr因此thisPtr引用计数增加 1;这 3 个智能指针现在的引用计数为 3。

5.1。然后,构造一个指向引用计数setParent为 1 的子节点的临时共享指针并调用父节点:addChild

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

5.2. addChild创建一个指向具有引用计数 1的父级的共享指针:

Message::Ptr thisPtr(this);

5.3. addChild返回,销毁 5.2 的共享指针,这会销毁父级,这会销毁父级的QSet<Message::Ptr> m_children成员。

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

更一般地说,您有一个循环引用:父母拥有孩子,孩子拥有他们的父母,这是内存泄漏和删除后使用错误的秘诀。构造新的共享指针并拥有其他共享指针已经拥有的原始指针是双重删除和删除后使用错误的秘诀;共享指针不会相互了解,它们的引用计数将独立变化。您应该调查QWeakPointer以打破循环并QEnableSharedFromThis安全地获得指向*this.

于 2017-01-24T20:31:03.047 回答