这个指针和QSharedPointer

时间:2017-01-24 19:58:51

标签: c++ qt qt5 shared-ptr qsharedpointer

我有一个类似于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;
};

此类可以包含父级和一组子级。我实施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. 使用引用计数1 创建
  3. thisPtr
  4. childPtr->parent() != thisPtr将被解析为true
  5. childPtr->setParent(thisPtr);Message::setParent被执行,thisPtr引用计数将增加1,因为创建了共享指针的副本。现在thisPtr的引用次数为2
  6. Message::setParent被执行时,m_parentPtr = parentPtr;会将m_parentPtrparentPtrthisPtr引用计数增加1;这3个智能指针现在的引用数为3。
  7. 执行退出Message::setParent并销毁parentPtr,将m_parentPtrthisPtr的引用次数减少1
  8. 执行返回Message::addChild。现在引用计数thisPtr为2。
  9. 实际发生的事情:

    当执行退出if Message::addChild中的thisPtr语句时,引用计数再次减少1,使thisPtr的引用计数为1.这会使所有内容都中断执行存在Message::addChildthisPtr被破坏,因此this被删除。

    我的问题:

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

    以下是它在调试器中的运行方式: enter image description here

1 个答案:

答案 0 :(得分:2)

  
      
  1. Message::setParent被执行时,m_parentPtr = parentPtr;会将m_parentPtrparentPtrthisPtr引用计数增加1;这3个智能指针现在的引用数为3。
  2.   

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的临时共享指针被销毁,这会破坏孩子。

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