我有一个类似网络的数据结构,由链接在一起的节点组成。
数量将更改的节点将以std::vector<Node>
的顺序存储,其中Node
是适当的类。
我想跟踪节点之间的链接。同样,这些链接的数量会发生变化,我正在考虑再次使用std::vector<Link>
。 Link
类必须包含有关它连接的两个节点的信息,以及其他链接功能。
Link
应该包含
std::vector<Node>
?第一种方法,尽管可能更好,但是有问题,因为每次我从网络添加或删除节点时都必须重新生成指针,但另一方面,这将使我免于死亡。将节点存储在随机访问容器中。
答案 0 :(得分:4)
一般来说这很难回答。有各种性能和易用性权衡。
使用指针可以为某些操作提供更方便的用法。例如
link.first->value
VS
nodes[link.first].value
使用指针可能提供比索引更好或更差的性能。这取决于各种因素。您需要进行测量以确定哪种情况更好。
如果可以保证只有一定数量的节点,则使用索引可以节省空间。然后,您可以使用较小的数据类型作为索引,而使用指针,无论您拥有多少个节点,都需要使用完整的指针大小。使用较小的数据类型可以通过允许更多链接适合单个缓存行来获得性能优势。
使用索引复制网络数据结构会更容易,因为您不必重新创建链接指针。
指向std::vector
元素的指针可能容易出错,因为向量可能会在插入后将元素移动到内存中的另一个位置。
使用索引将允许您进行边界检查,这可以更容易地找到一些错误。
使用索引使序列化更加直接。
所有这一切,我经常发现指数是整体的最佳选择。使用便捷方法可以克服索引的许多语法上的不便,并且可以在指针具有更好性能的某些操作期间将索引切换为指针。
答案 1 :(得分:2)
指定要使用或创建的类的接口。写单元测试。做最简单的事情来完成单元测试。
所以它取决于类的接口。例如,如果Link
不导出有关节点的信息,那么您选择的方法并不重要。另一方面,如果你去寻找指针,请考虑std::shared_ptr。
答案 2 :(得分:1)
我会向你的Node类添加一个(或许多)link
指针,然后手工维护链接。这样可以节省您使用额外容器的费用。
如果您正在寻找更有条理的内容,可以尝试使用Boost Intrusive。这有效地以更一般化的方式做同样的事情。
答案 3 :(得分:1)
如果使用以下内容,则可以完全避免使用Link
类:
struct Node
{
std::vector<Node*> parents;
std::vector<Node*> children;
};
采用这种方法,
Node
s。下行。你必须确保:
Node
时,您必须从parents
和children
删除指针。答案 4 :(得分:1)
您可以将其设为std::vector<Node *>
而不是std::vector<Node>
,并使用new
分配节点。
然后:
您可以将指针存储到Link
类中的节点,而不必担心它们会失效
您仍然可以在节点向量中随机访问它们。
缺点是当从节点列表中删除它们时,您需要记住它们delete
。
答案 5 :(得分:1)
我对使用图形结构中的向量的个人经验提出了这些不变量。
你有一个像数据结构的图表。如果代码对性能不敏感(这与性能敏感性不同!),则不应考虑缓存压缩数据结构。
如果您不知道图表的大小,并且您在向量中获得了Node
数据,则一旦向量调用vector::reallocate()
,所有迭代器和指针都将失效,这意味着你必须以某种方式重新生成你的整个数据结构,也许你必须创建所有数据的副本,并使用dfs或类似来调整指针。如果你想删除一个向量中间的数据,也会发生同样的事情。
如果你知道自己的数据有多大,那么你就会保持这种状态,或者一旦你重新考虑就会有很大的麻烦。
如果您有一个类似于数据结构的图表并且您在性能关键路径上删除了,则只要您的算法决定他不再需要数据,就调用delete是不明智的。一种可能性是将数据保存在堆上(如果它是性能关键的,考虑池分配器)标记在性能关键部分中不再需要的对象(如果你真的需要节省空间,可以考虑指针标记)或之后使用一些简单的标记和扫描算法来查找不再需要的项目(是的图形算法是sutter说垃圾收集比智能指针更快的情况之一)。
请注意,对象的延迟销毁意味着您在Node类中丢失了所有类似RAII的功能。