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

时间:2014-10-07 07:10:03

标签: c++ qt pointers memory-management smart-pointers

我有一个课程,它拥有它的孩子的所有权:

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方法添加到Parent类的

addChild个实例将在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个继承的类,这使得它对这种情况毫无用处。我怎么能阻止这个?

6 个答案:

答案 0 :(得分:5)

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

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

答案 1 :(得分:1)

如果你看一下constructor of QObject,你会发现它需要一个QObject作为父母,所以不要重新发明轮子,除非你已经在使用它(看起来你不是),您可以使用Qt的父子层次结构: -

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

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

如果要在创建子项目后设置子项,请调用子项的setParent函数。

答案 2 :(得分:0)

您可以使用std::tr1::shared_pointer here作为教程。如果你的编译器中没有这个,你可以使用boost::shared_pointer,描述here你也可以implement自己的智能指针。

答案 3 :(得分:0)

我猜你的第一个问题是关于子元素的所有权。 如果您希望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 );
}

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

答案 4 :(得分:0)

Parent取得Child的所有权,而不应放在样本中。

可能的解决方案包括:

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

答案 5 :(得分:0)

这是使用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;

}

注意:这不会让父母控制孩子的生命周期。 Parent对象可以与Child对象关联,但是现在只有在对对象的最后一个引用被销毁时才会销毁它。如果上面显示的是john的情况,如果其他东西保留在指向它的智能指针上,则孩子可能“幸存”其所有父母。如果您的目的是让Child只属于一个Parent并且使用它的Parent进行破坏,那么您应该将Parent创建逻辑封装在Parent中,这样Child对象只能由Parent对象创建。