在现代C ++中维护“ widgets”树的最佳方法是什么?当前,我为子节点使用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
并让其父元素拥有子元素。在一般情况下,我同意,但是如果您有一棵“小部件”树,我不确定那将如何工作。对于“小部件”树,我的意思是像您在GUI代码。它是通过在各处添加节点来手动构建的,外部代码经常必须访问各个节点。如果使用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
。我还有其他习语吗? 最佳答案
您可以使addChild
的行为类似于emplace
方法,并使其在内部创建unique_ptr
,然后返回非所有者指针(或引用)。即就像是:
template <class... Args>
Element & Element::addChild (Args && ... args) {
children_.push_back(std::make_unique<Element>(std::forward<Args>(args)...));
return children_.back();
}
因此,现在您可以执行以下操作而不会在调用站点上暴露任何指针材料:
Element blah () {
Element canvas("canvas");
Element & button = canvas.addChild("button");
button.manipulate();
return canvas;
}
编辑:使示例
addChild
代码更简洁。