在现代C ++中维护“ widgets”树的最佳方法是什么?当前,我为子节点使用vectorshared_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代码更简洁。

09-10 00:21