我正在尝试使用std::unique_ptr
重新实现树数据结构,并且想法是父节点将拥有其子节点,这些子节点存储在unique_ptr
的向量中。
由于接口原因,我需要一种节点会破坏自身的方法。在这种情况下,我认为节点要从父对象中的子向量中删除自己。
以下实现'有效'(在c ++ 11编译器中),但它很丑陋,我确信它是处理这个问题的次优解决方法。
#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>
struct Node {
typedef std::vector<std::unique_ptr<Node>> vec_node_uptr;
unsigned id;
Node* parent;
vec_node_uptr child_nodes;
// ctor
Node(unsigned id): id(id){ parent = nullptr; }
void add_child(Node* new_child){
new_child -> parent = this;
child_nodes.push_back( std::unique_ptr<Node>(std::move(new_child) ) );
}
int where_am_i(){
int result_ = 0;
for(auto& i: this -> parent -> child_nodes) {
if (this == i.get()) {
return result_;
} else {
result_++;
}
}
}
void suicide(){
parent -> child_nodes.erase(parent -> child_nodes.begin()+ where_am_i());
}
};
int main()
{
std::unique_ptr<Node> root(new Node(0));
root -> add_child(new Node(1));
root -> add_child(new Node(2));
root -> child_nodes[0] -> add_child(new Node(3));
root -> child_nodes[0] -> add_child(new Node(4));
root -> child_nodes[1] -> add_child(new Node(5));
root -> child_nodes[1] -> add_child(new Node(6));
root -> child_nodes[1] -> suicide();
return 0;
}
有什么建议吗?也许使用std::find
?
答案 0 :(得分:1)
使用find_if和lambda:
可以更优雅地解决这个问题void suicide()
{
auto& parentsChildren = parent->child_nodes;
parentsChildren.erase(find_if(begin(parentsChildren), end(parentsChildren),
[&](const unique_ptr<Node>& node) { return node.get() == this; }));
}
答案 1 :(得分:1)
如果想要使用当前数据结构的常量where_am_i()
,则需要在节点本身中存储索引或迭代器。这是(a)重复和(b)将导致进一步的复杂化,因为无论何时删除不是其父项的最后一个子节点的节点,您将需要更新所有后续子节点的索引/迭代器...
然后,可能有一个恒定时间where_am_i()
没有真正的优势,因为从向量中删除元素无论如何都是O(n),除非你总是从末尾删除(或接近结束)。
但是如果你通常会从最后删除,并且如果永远不需要将一组孩子的所有权转移到父节点之外,那么这里有一个替代的,更简单的设计,可以避免存储索引或每个节点中的迭代器:
C ++标准保证std::vector
和数组一样,它们的内容将在内存中连续排列。因此,如果child_nodes
向量实际按值存储其元素 - 即如果它被声明为
typedef std::vector<Node> vec_node_uptr;
vec_node_uptr child_nodes;
那么你可以通过简单地从给定元素的地址中减去向量中第一个元素的地址来找到恒定时间内的位置,让指针算术为你做除法:
size_t where_am_i() {
return this - &parent->child_nodes[0];
}