【问题标题】:How to manipulate an instance of a class which contains a `unique_ptr`?如何操作包含`unique_ptr`的类的实例?
【发布时间】:2017-05-21 21:24:24
【问题描述】:

我有一个如下所示的结构:

struct TreeNode
{
    int value;
    std::unique_ptr<TreeNode> left = nullptr, right = nullptr;
};

对于这样的树实现,我想使用unique_ptr 比使用shared_ptr 更好,对吧?

然后我在priority_queue中放了几个节点,比如这样:

bool operator<(const TreeNode& t1, const TreeNode& t2)
{
    return t1.value < t2.value;
}

...

std::priority_queue<TreeNode> nodes;

for(int i = 0; i < 10; i++)
{
    TreeNode node;
    node.value = i;
    nodes.push(node);
}

在那之后,unique_ptr 的问题出现在我必须做这样的事情时:

TreeNode n1 = nodes.top();
nodes.pop();
TreeNode n2 = nodes.top();
nodes.pop();

TreeNode node;
node.value = n1.value * n2.value;
node.left = std::unique_ptr<TreeNode>(new TreeNode(n1));
node.right = std::unique_ptr<TreeNode>(new TreeNode(n2));

此代码显然不起作用,但我想知道如何使其尽可能简单地工作。

我设法通过从 Internet 获得的 make_unique 实现做了一些事情,但这太临时了。我也尝试过使用std::movestd::forward,但没有成功。

此外,我以后必须做这样的事情:

std::unique_ptr<TreeNode> root(new HuffmanTreeNode(my_root));

std::unique_ptr<TreeNode> current_node = node;

while(current_node)
{
    current_node = current_node->left;
}

再一次,它显然不起作用,但我不知道如何使它正常工作。 (算法也是废话,只是为了说明我的问题)

您能举个例子,告诉我如何在这里完成吗?也许有某种方法可以将unique_ptr 转换为shared_ptr,然后进行反向操作?

【问题讨论】:

  • std::unique_ptr 不可复制,故事结束。如果你想让你的结构可复制,那么你不能使用std::unique_ptr。或者您需要考虑另一种不需要复制您的结构的设计。
  • 没错,这不是我真正想做的。我修改了我的标题。我想用shared_ptr 实现树形结构是不行的,但我想还是有办法做我需要做的操作。
  • 这取决于您要放入队列的节点是否附加到树上。如果没有,您可能需要priority_queue&lt;unique_ptr&lt;TreeNode&gt;&gt;,或者将树数据与树节点分开并拥有priority_queue&lt;TreeData&gt;

标签: c++ pointers unique-ptr


【解决方案1】:

TreeNode 没有复制构造函数(因为隐式构造函数格式不正确,因为 leftright 不可复制)但它确实有一个隐式移动构造函数,它将移动 @987654324 @ 和 right 到新对象中。您有几个地方可以尝试复制:

std::priority_queue<TreeNode> nodes;

for(int i = 0; i < 10; i++)
{
    TreeNode node;
    node.value = i;
    nodes.push(node); // Attempts copy of node
}

// ...

TreeNode n1 = nodes.top(); // Attempts copy of nodes.top()
nodes.pop();
TreeNode n2 = nodes.top(); // Attempts copy of nodes.top()
nodes.pop();

TreeNode node;
node.value = n1.value * n2.value;
// The following two lines attempt to copy n1 and n2, respectively.
node.left = std::unique_ptr<TreeNode>(new TreeNode(n1));
node.right = std::unique_ptr<TreeNode>(new TreeNode(n2));

以下代码中还有其他一些地方,但你明白了。

请注意,您可以std::move()您自己的节点来解决错误:

std::priority_queue<TreeNode> nodes;

for(int i = 0; i < 10; i++)
{
    TreeNode node;
    node.value = i;
    // Use emplace to directly-construct the element in the queue.
    nodes.emplace(std::move(node));
}

// ...

// priority_queue<T>::top() gives us a const reference; we have to cast
// the const-ness away (which is legal in this case) to move from it.
// See http://stackoverflow.com/a/20149745/501250
TreeNode n1{std::move(const_cast<TreeNode &>(nodes.top()))};
nodes.pop();
TreeNode n2{std::move(const_cast<TreeNode &>(nodes.top()))};
nodes.pop();

TreeNode node;
node.value = n1.value * n2.value;
node.left = std::unique_ptr<TreeNode>(new TreeNode(std::move(n1)));
node.right = std::unique_ptr<TreeNode>(new TreeNode(std::move(n2)));

【讨论】:

  • 还有TreeNode* current_node = node.get();
  • 是的,我不理会那个块,因为我不能 100% 确定它要完成什么。我更正了前两个块中明显的副本,希望能给 OP 提供足够的推动力,使其朝着正确的方向理解问题和解决方案,并希望他们可以将自己应用到他们的其余代码中。
  • 谢谢,它工作正常。不过我有几个问题:虽然nodes.push(node) 复制了node,但std::unique_ptr 在这里是nullptr,所以它不会产生错误。怎么样更好?然后关于const_cast,我理解“法律”方面,但是nodes.top() 元素在移出后会发生什么?我们仍然需要弹出它,所以它仍然在这里但没有指针吗?还有关于TreeNode* current_node = node.get();,我们为什么要在这里使用原始指针?
  • @Yksuh 您根本无法复制std::unique_ptr,无论它是否包含对象——它没有复制构造函数,句号。从nodes.top() 对象移出后,其leftright 成员指向nullptr。正确,从对象移动并不会破坏对象,它会“窃取”对象的内容。我怀疑您想使用原始指针,因为 std::unique_ptr 是一个 owning 指针,当它超出范围时将删除其内容。从 unique_ptr 获取原始指针有效地“借用”了指针的非拥有副本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多