【发布时间】:2018-06-30 01:53:26
【问题描述】:
背景:
因此,我一直在将一些较旧的 Java 代码移植到 C++ 中,但遇到了一个问题,这让我很难进行。我的项目使用树数据结构来表示 3D 动画的节点层次结构。
Java:
public final class Node {
private final Node mParent;
private final ArrayList<Node> mChildren;
//private other data, add/remove children / parents, etc ...
}
在 Java 中,创建允许修改等的树非常简单。
问题:
我遇到了 C++ 的问题,如果不手动分配新的内存块并移动现有的内存块,就无法轻松添加数组,因此我切换到 std::vector。向量的问题是我刚才在内部描述的操作会使指向该元素的任何指针无效。所以基本上如果你不想使用指针,你需要一种方法来支持它们,这样保存实际节点的内存就不会移动。我认为您可以使用std::shared_ptr/std::unique_ptr 将节点包装在std::vector 中,我尝试使用这种方法,但它变得非常笨拙。另一种选择是有一个“树”类来包装节点类并且是操作它的接口,但是(对于我的用例)处理切断分支并将它们放入自己的树中会很烦人并可能附加不同的分支。
我在网上看到的大多数示例是具有 2 个节点而不是动态的二叉树,或者它们有许多关于内存泄漏 / 等的 cmet。我希望上面显示的 java 代码有一个很好的 C++ 替代方案(没有内存泄漏问题等)。此外,我不会进行任何排序,树的目的是维护层次结构而不是对其进行排序。
老实说,我真的不确定该往哪个方向发展,过去 2 天我一直在尝试不同的方法,但没有一个“感觉”正确,而且通常很难管理,任何帮助将不胜感激!
编辑:
关于为什么 shared_ptrs 不实用的编辑:
class tree : std::enable_shared_from_this<tree> {
std::shared_ptr<tree> parent;
std::vector<std::shared_ptr<tree>> children;
public:
void set_parent(tree& _tree) {
auto this_shared_ptr = shared_from_this();
if (parent != nullptr) {
auto vec = parent->children;
auto begin = vec.begin();
auto end = vec.end();
auto index = std::distance(begin, std::find_if(begin, end, [&](std::shared_ptr<tree> const& current) -> bool {
return *current == this_shared_ptr;
}));
vec.erase(std::remove(begin, end, index), end);
}
parent = std::shared_ptr<tree>(&_tree);
if (parent != nullptr) {
parent->children.push_back(this_shared_ptr);
}
}
};
使用像上面这样的指针变得非常冗长,我希望有一个更简单的解决方案。
【问题讨论】:
-
尝试
std::list而不是std::vector以获得快速而肮脏的解决方案。如果列表中的元素被删除,指向列表元素的指针不会失效。但同样,如果您可以使用 std::list,这又快又脏。 -
一个节点最多可以拥有多少个子节点?您是否非常关心性能(1),(2)一点点,或者(3)不是真的?您需要能够从树中删除节点,还是只添加它们?
-
@JohnZwinck 这将主要是一个只读树,因此如果这意味着迭代会更快,那么对结构的修改可能会更慢。至于子节点的最大数量,并没有明确定义的最大值,每个节点可以有任意数量的子节点。最好能够添加/删除节点,但是一旦构建了树,这些操作就不太常见了。
-
std::shared_ptr 到底有什么“笨拙”的地方?唯一的问题是,您必须弄清楚如何处理循环引用。对此的经典解决方案是对父节点使用弱指针。
-
如果
std::shared_ptr<tree>无处不在,请使用别名。