我有一个拥有其子级所有权的类:
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 对象现在只有在最后一个引用被销毁时才会被销毁。如果其他东西像上面显示的约翰那样,一个孩子有可能"生存"所有的父母,就像上面显示的约翰一样。如果你的意图是让一个子对象只属于一个父对象,并且要用它的父级进行破坏,则应将子项创建逻辑封装在父项中,以便子对象只能由父对象创建。