【发布时间】:2017-11-18 12:41:45
【问题描述】:
在现代 C++ 中维护“小部件”树的最佳方法是什么?目前,我对子节点使用vector 或shared_ptr,并使用指向父节点的普通指针。它是这样使用的:
auto canvas = make_shared<Element>("canvas");
auto button = make_shared<Element>("button");
canvas->addChild(button);
// do something with either canvas or button
我很满意,但我不喜欢make_shared<Element> 仪式的必要性。
我还读到您 should use unique_ptr 并让父元素拥有子元素。我同意一般情况,但如果你有一个“小部件”树,我不确定它是如何工作的。对于“小部件”树,我的意思是像你在例如图形用户界面代码。它是通过在这里和那里添加节点来手动构建的,并且外部代码经常必须访问单个节点。如果我使用unique_ptr,对addChild 的调用将移动指针,上面的代码将不再能够使用子对象。我可以让addChild 再次返回一个常规指针,但这会使调用站点更加复杂。
理想情况下,我可以这样说:
Element blah() {
// this could also be changed to Element* or some_pointer<Element>,
// but I want the call site to be clean and simple and not leak
// implementation details
Element canvas("canvas");
Element button("button");
canvas.addChild(button);
button.manipulate();
return canvas;
}
auto canvas = blah();
canvas.children[0].manipulate();
当然,这在 C++ 中是行不通的。要么addChild 复制,但随后我在“blah”中操作了错误的按钮实例。或者它需要一个指针,但随后它变得悬空并且操作失败。我需要一个神奇的操作,将button 的所有权转移到canvas,并将button 替换为指向真实按钮的外壳对象。为此,我必须将 Element 替换为一种伪造引用语义的智能容器,并将真实对象隐藏在其中(我认为 C++/WinRT 会这样做)。但是,这似乎很单调,这就是我选择shared_ptr 的原因。我还缺少其他成语吗?
【问题讨论】:
-
你为什么不喜欢
make_shared<Element>,就要把鼹鼠山变成一座山? -
@EdHeal:我发现在这种情况下可以使用不同的模式,我想了解每种模式的优缺点。这是我想出的,但是我读到有人建议不要以这种方式使用
shared_ptr。也许这很好,也许有更好的方法。我不是“以鼹鼠山造山”。没有大问题,我没有抱怨,只是想学习一些东西并提高我的手艺。 -
如果您需要对对象的生命周期进行如此明确的控制,我认为智能指针只是错误的方法。我只使用
std::vector<Element*> children,防止复制,确保将deletes 很好地封装在Element的析构函数中,并确保每个Element都必须使用Parent指针参数构造。我也不会提供addChild;永远不应该有一个Element不附属于父母,甚至是短暂的。 -
@ChristianHackl:问题是,我不需要显式控制对象的生命周期。我对生命的担忧越少越好。我想尽可能避免出现悬空或无效指针。我需要的是拥有未附加元素的能力,或者分离子树,使用它,然后在其他地方附加它。想想 DOM 节点。如果不是这样,我会采用你的方法。
-
看看 Qt 如何处理这个问题可能很有启发性。
标签: c++