如何使用智能指针防止双重对象删除



我有一个拥有其子级所有权的类:

class Child
{
public:
    Child() {}
    ~Child() {}
};
class Parent : public QObject
{
    Q_OBJECT
public:
    explicit Parent(QObject *parent = 0): QObject(parent) {}
    ~Parent()
    {
        qDeleteAll(m_children);
    }
    void addChild(Child *ch)
    {
        m_children.append(ch);
    }    
private:
    QList<Child *> m_children;    
};

Child使用addChild方法添加到 Parent 类的实例将在删除Parent时被删除。

以下用法将导致双重子项销毁:

int main()
{
    {
        Parent father;
        Child child;
        father.addChild( &child );
    }
    //child is out of scope now and gets destroyed
    //father gets destroyed too
    //father's destructor deletes child AGAIN!
    //crash!
    return 0;
}

如何使用智能指针来防止这种情况? QPointer可用于QObject继承的类,这使得它在这种情况下毫无用处。我还能如何防止这种情况?

这不是关于双重破坏。您无法删除堆栈对象。相反,您应该在堆中分配它:

Parent father;
Child* child = new Child();
father.addChild( child );

如果你看一下 QObject 的构造函数,你会注意到它采用 QObject 作为父级,所以与其重新发明轮子,除非你已经在使用它(看起来你没有(,你可以使用 Qt 的父子层次结构: -

Parent* parent = new Parent;
Child* Child1 = new Child(parent);

然后,您可以根据需要检索父项的子项列表,而不必担心管理子项,因为它们会在删除父项时被删除。

如果要在创建子项

后设置子项的父项,可以调用子项的 setParent 函数。

你可以使用std::tr1::shared_pointer这里有一个教程。如果您的编译器中没有,您可以使用 boost::shared_pointer ,描述在这里 你也可以实现自己的智能指针。

我想你的第一个问题是关于子元素的所有权。如果希望 Parent 类处理 Child 生存期,可以使用智能指针来解决它。

class Parent : public QObject
{
    using ptr = std::shared_ptr<Child>;
    Q_OBJECT
public:
    explicit Parent(QObject *parent = 0): QObject(parent) {}
    void addChild(const ptr& ch)
    {
        m_children.append(ch);
    }    
private:
    QList<ptr> m_children;    
};

然后:

{
    Parent father;
    auto child = std::make_shared<Child>();
    father.addChild( child );
}

如果要保留实际使用情况,则必须制作副本并存储这些副本。

Parent

不应该出现在样本中的地方拥有Child的所有权。

可能的解决方案包括:

  • Child应在其析构函数中将自身从父级中删除,以防止错误删除。
  • 修改Parent接口以明确强制所有权转移:void addChild(std::share_ptr<Child> ch)void addChild(std::unique_ptr<Child> ch)
  • Parent中维护 2 个容器,一个有所有权,一个没有所有权。

以下是使用 std::shared_ptr 的可能解决方案:

    #include <iostream>
    #include <memory>
    #include <vector>
    class Child
    {
        public:
            Child(std::string name) 
            {
                name_ = name;
            }
            ~Child() 
            {
                std::cout << "~Child: " << name_ << std::endl;  
            }
        private:
            std::string name_;
    };
    class Parent 
    {
        public:
            ~Parent()
            {
                std::cout << "~Parent" << std::endl;    
            }
            void addChild(std::shared_ptr<Child> ch)
            {
                m_children.push_back(ch);
            }    
        private:
            std::vector <std::shared_ptr<Child> >  m_children;    
    };

    int main()
    {
        std::shared_ptr<Child> john (new Child("John")); 
        std::cout << "John created" << std::endl;
        {
            Parent father;
            {
                std::shared_ptr<Child> jack ( new Child("Jack")); 
                std::cout << "Jack created" << std::endl;
                father.addChild( john );
                father.addChild( jack );
                std::cout << "added John and Jack to father" << std::endl;
            }
            std::cout << "jack left scope" << std::endl;
            // jack is out of scope now but since it's a smart pointer 
            // and father still holds a reference to the memory it 
            // referred to that memory is still allocated.
        }
        std::cout << "father left scope" << std::endl;
        // father gets destroyed here and with it its m_children.  
        // As the list goes so does the last reference to the memory 
        // allocated for child and the child gets destroyed.  
        // Note that jack doesn't get destroyed here. Even though it was 
        // one of the children.  The reason is that the orphan smart 
        // pointer is still in scope.
        return 0;
}

注意:这不会让父级控制其子项的生命周期。 父对象可以关联到 Child 对象,但 Child 对象现在只有在最后一个引用被销毁时才会被销毁。如果其他东西像上面显示的约翰那样,一个孩子有可能"生存"所有的父母,就像上面显示的约翰一样。如果你的意图是让一个子对象只属于一个父对象,并且要用它的父级进行破坏,则应将子项创建逻辑封装在父项中,以便子对象只能由父对象创建。

相关内容

  • 没有找到相关文章

最新更新