C++11如何通过原始指针或引用获取unique_ptr的所有权?

4

编辑
我想下面的代码会假设我有一个重载版本的addChild(),它接受一个已经包装在unique_ptr中的Sprite,其中拥有所有权是可以的。只是想在别人之前提一下。:) 我在非常漫长的一天后编写了所有的代码,所以请将其视为伪代码质量,仅用于展示手头的问题。

原始问题

我正在编写一个框架,其中包含显示列表、父/子等。我认为在这里使用例如unique_ptr<Sprite>是正确的方式,因为当您向父显示对象添加一个子对象时,逻辑上只有父对象现在成为该子对象的唯一所有者。

然而,将有可用的方法,如getChildAt(index)getChildByName等,我认为应该返回引用或指针值,因为这些方法只是用于将子项暴露给操作,而不是转移所有权。

最后,问题和这个问题的原因在于以下情况。让我们假设我们有两个Sprite对象,它们都是显示列表根目录Stage的子对象。假设我们有第三个子Sprite。

Stage newStage;
std::unique_ptr<Sprite> parentOne(new Sprite);
std::unique_ptr<Sprite> parentTwo(new Sprite);
newStage.addChild(parentOne); //Stage takes ownership of parentOne
newStage.addChild(parentTwo); //Stage takes ownership of parentTwo

std::unique_ptr<Sprite> someChild(new Sprite);
parentOne->addChild(someChild) //parentOne takes ownership of someChild.

现在,假设在使用这个框架的游戏或其他代码库的某个地方,通过getChildAt(int index);方法访问someChild

Sprite& child = parentOne->getChildAt(0);

以下的情况是完全合法的:
parentTwo->addChild(child);
addChild方法会处理将子项从其先前的父项中移除(如果存在父项),以便新的父项现在可以使该子项成为其显示列表部分的一部分。
我将每个精灵的子项作为引用或指针返回,因为我们不想在方法(如getChildAt())中交出所有权,只是提供对子项的访问。我们不想将其作为unique_ptr交出,并使其超出范围并死亡。
但是,正如我所说,现在可以将此子项(现在通过引用或指针访问)传递给另一个容器(假设在拖放操作中,从一个列表中拖动项目到另一个列表中)。我们现在面临的问题是,唯一所有权需要从一个父项转移到另一个父项,但我们只有一个引用或原始指针。
我想知道这个问题的合理解决方案是什么。如果我返回一个指针,是否可能在这个阶段正确地转移所有权?
void Sprite::addChild(Sprite* newChildToOwn)
{
    /* by checking newChildToOwn->parent we can see that the child is already owned
    by someone else. We need to not only remove the child from that parents' part of the
    display list and add it here, but transfer exclusive object ownership of newChildToOwn
    to this->.
    */
}

我是否漏掉了什么,或者为什么你不利用移动语义呢? - imreal
在Sprite::addChild(Sprite* someOtherSprite)的作用域内,没有引用已经管理someOtherSprite的unique_ptr。 - user562566
请参阅以下答案和DrYaps回答的讨论获取更多信息。 - user562566
我会称之为 moveChildtakeChild,并确保它以 move 的方式接收其参数(可以使用右值引用或 unique_ptr)。 - Yakk - Adam Nevraumont
2个回答

6

+1。你和DrYap指向了完全相同的解决方案。太好了,现在我只需要想办法标记为正确答案,因为你们两个都指向了解决方案的位置。 - user562566

3
为了转移所有权,您需要访问unique_ptr,因为原始指针不知道它的使用方式。
将remove child方法返回unique_ptr是否有效?这将在传输期间保持对象活动,并使Sprite能够拥有所有权。这将允许引用在其他地方使用,就像您已经做过的那样。

非常有趣。实际上,removeChild() 应该返回一个 unique_ptr 指向子对象,因为一旦移除,如果对象没有被捕获到其他地方使用,它应该死亡。我想我可以在 addChild() 中简单地执行 child->parent->removeChild(child) 来获取 unique_ptr。 - user562566
没错。顺便提一下,要小心使用原始引用,因为如果对象从树中完全删除,这些引用可能会变得无效。在这种情况下,shared_ptr 更加合适。 - DrYap

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接