Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/148.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 删除父/子窗口层次结构的最佳方法_C++_Vector_Parent_Hierarchy - Fatal编程技术网

C++ 删除父/子窗口层次结构的最佳方法

C++ 删除父/子窗口层次结构的最佳方法,c++,vector,parent,hierarchy,C++,Vector,Parent,Hierarchy,我在GUI中有一个窗口系统,我不确定销毁顺序。每个窗口都有一个包含其子窗口的向量,每个窗口还有一个指向其父窗口的指针: auto root = new Window; root->addChild(new Window); root->addChild(new Window); auto child = root->addChild(new Window); // Return value is the newly created Window child->addCh

我在GUI中有一个窗口系统,我不确定销毁顺序。每个窗口都有一个包含其子窗口的向量,每个窗口还有一个指向其父窗口的指针:

auto root = new Window;
root->addChild(new Window);
root->addChild(new Window);
auto child = root->addChild(new Window);  // Return value is the newly created Window
child->addChild(new Window);
child->addChild(new Window);
auto grandchild = child->addChild(new Window);
grandchild->addChild(new Window);
grandchild->addChild(new Window);
grandchild->addChild(new Window);
//我想删除子指针,首先我需要从其父对象的子向量中删除它的指针

child->parent->children.erase(child->parent->children.begin() + child->positionInParentsVector - 1);

child->destroy();

void Window::destroy()
{
    if (children.size() == 0) delete this;
    else for (auto i : children) i->destroy();
}
或者使用智能指针向量,是否足以:

// Remove reference of child from parent's children vector, then

delete child;

我已经读到可以删除这个。我很难理解这一点。

我想你有点弄糊涂了。以下自包含示例没有内存泄漏:

#include <utility>
#include <vector>
#include <memory>

using std::vector;
using std::unique_ptr;

struct Window {
    Window() = default;
    vector<unique_ptr<Window>> children;
    Window* parent = nullptr;
    ~Window();

    Window* addChild(std::unique_ptr<Window> c) {
        c->parent = this;
        children.push_back(std::move(c));
        return children.back().get();
    }
};

Window::~Window() = default;

int main() {
    auto root = std::make_unique<Window>();
    root->addChild(std::make_unique<Window>());
    auto child = root->addChild(std::make_unique<Window>());
    child->addChild(std::make_unique<Window>());
};
#包括
#包括
#包括
使用std::vector;
使用std::unique\u ptr;
结构窗口{
Window()=默认值;
媒介儿童;
Window*parent=nullptr;
~Window();
窗口*添加子对象(标准::唯一\u ptr c){
c->parent=this;
儿童。推回(标准::移动(c));
返回children.back().get();
}
};
窗口::~Window()=默认值;
int main(){
auto root=std::make_unique();
root->addChild(std::make_unique());
auto child=root->addChild(std::make_unique());
child->addChild(std::make_unique());
};
类不能包含自身,但它可以包含自身(或其容器)的
唯一\u ptr
。在这个方案中,所有的递归销毁都正确自动地进行。当然,你可以用各种方式来装扮它,但这是最基本的想法

这里还有一些关于良好的对象和类设计以及封装的问题,但这是另一个问题。注意:在绝大多数情况下,您不应该使用名为
destroy
的方法,而应该在析构函数中使用

基本上不需要在C++11或更高版本中编写new或delete,除非您编写的代码级别相对较低(比如您自己的内存分配器)


实例:

智能指针可能是最好的,所以它会自动清理。但是,如果层次结构很深,则析构函数中可能会出现堆栈溢出。在这种情况下,您将需要像您所展示的那样“手动”释放所有内容。此外,由于destroy函数是递归的,我只需要在最高级别删除父级引用一次,这意味着我需要创建一个函数来删除引用,另一个函数来执行递归删除,对吗?这可能是三个函数,一个removeReferenceAndDestroy,在这个removeReference中,然后执行递归destroy()。如果堆栈溢出是一个现实问题(它也会发生在常规的层次递归遍历中),那么它实际上并不像您所展示的那样,您需要不使用递归而使用循环来执行此操作。基本上,智能指针dtor将自动执行您所执行的操作。如果您有一个
addChild
,您不应该有一个相应的
removeChild
来让父级执行清理吗?对我来说,
child->destroy()
似乎是从
delete child
调用析构函数时应该做的事情(但是没有
删除这个
)。谢谢。有趣的是,大多数人倾向于远离原始指针,以及C++的趋势。我想这对自动清理来说是件好事,肯定容易多了。智能指针向量总是一个额外的调用,对吗?销毁向量后,将调用每个唯一_指针的析构函数,然后对指向的对象调用delete。@Zebrafish您必须小心。人们远离原始指针。我的代码既包含原始指针成员变量(通常是一件坏事,但有时您需要它),也包含从成员变量返回的指针。然而,在这两种情况下,它都是非所有权关系。请注意,在
addChild
中,最好检查
c
是否确实拥有一个
窗口,如果没有,则抛出。然后,您可以返回一个
窗口&
,该窗口更好,因为它不能为空(如果您好奇,我可以更改代码)。@Zebrafish一个额外的调用,相对于什么?您的描述是正确的,但我不认为如果使用手动而不是自动清理编码,它会更快。当您std::make_unique将其作为新窗口时,它怎么可能不包含窗口?你是说如果分配失败?另外,我不明白为什么返回对窗口的引用而不是指针会有不同。@Zebrafish我不确定你的第一条评论。我说“一个类不能包含它自己”。窗口没有窗口成员,但可以有唯一的\u ptr成员。独特的_ptr有一个间接的方向,使这项工作。引用更好,因为它不能为null,而且使用null窗口也没有任何意义。就像有人调用
root->addChild(std::unique_ptr{}),您可能希望它引发异常。