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

问题描述 投票:0回答:6

我有一个班级,它拥有它的孩子:

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
继承类,这使得它对于这种情况毫无用处。我还能怎样防止这种情况发生?

c++ qt pointers memory-management smart-pointers
6个回答
5
投票

这与双重销毁无关。您无法删除堆栈对象。 相反,你应该在堆中分配它:

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

1
投票

如果您查看 QObject 的 构造函数,您会注意到它以 QObject 作为父级,因此而不是重新发明轮子,除非您已经在使用它(看起来您还没有),您可以使用 Qt 的父子层次结构:

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

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

如果您想在创建子级后设置其父级,可以调用子级的 setParent 函数。


0
投票

你可以使用

std::tr1::shared_pointer
这里是一个教程。如果您的编译器中不提供该功能,您可以使用
boost::shared_pointer
,如here所述,您也可以实现您自己的智能指针。


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 );
}

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


0
投票

Parent
取得了
Child
的所有权,而它不应该出现在您的样本中。

可能的解决方案包括:

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

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;

}

注意:这并不能让父级控制其子级的生命周期。 父对象可以与子对象关联,但子对象现在仅在对其的最后一个引用被销毁时才会被销毁。如果有其他东西持有指向它的智能指针(如上面所示的 john 的情况),则孩子有可能“幸存”其所有父母。如果您的目的是让子对象仅属于一个父对象并与其父对象一起销毁,则应将子对象创建逻辑封装在父对象中,以便子对象只能由父对象创建。

© www.soinside.com 2019 - 2024. All rights reserved.