在STL列表中存储的每个项中存储对容器对象的引用的最有效方法

Most efficient way to store reference to container object in each item stored in STL list

本文关键字:存储 对象 引用 方法 有效 列表 STL      更新时间:2023-10-16

假设我有这个基本设置:

#include <list>
struct Linker
{
 Linker* to;
 //some Linker specific stuff
};
struct Holder
{
 std::list<Linker> links;
 //some Holder specific stuff
 //If I access the "to" variable in a Linker, I want to be able to access the data of the Holder that contains the Linker
};

存储在指向其他对象的列表中的简单对象,在这种情况下是相同类型的。当我访问链接器中的to并获取另一个链接器时,我希望能够弄清楚其他链接器在哪个Holder中,并访问该链接器的数据。

在阅读了Will的回答后,我应该明确指出,我并没有试图模拟另一个列表。在实际实现中,并非Holder列表中的每一项都是Linker,我仍然需要通过列表访问所有内容(使用特定于订单的迭代)。但这个列表可以处理这个问题,所以没关系。我需要在Holder类的列表结构之上的不同列表/持有者中的项目之间进行一定程度的零星链接。

最好的方法是什么?我曾考虑在每个Linker中放置Holder引用或指针,但假设我想将像Linker这样的结构与容器的数据分开(有时在没有容器的其他上下文中使用它们,希望在没有容器信息的情况下在外部构建,然后让Holder使用添加函数来设置该信息,等等)。

我考虑过使用std::pair和Holder引用或指针作为另一种类型,或者将Linker扩展到类似HeldLinker的信息类型。也考虑过将类似的东西用于比std::pair:更具体的变量名

template<typename R, typename O> struct refwrap
{
 R& ref;
 O obj;
 refwrap(R& ref, const O& obj) : ref(ref), obj(obj) {}
};

在所有这些情况下,Linker* tostd::list<Linker> links都会被修改为使用适当的类(std::pair、HeldLinker、refwrap等)。然而,似乎所有这些"解决方案"都会导致这样的功能:

void Holder::addlink(const Linker& link)
{
 //wrap link into whatever will hold a Holder reference/pointer
 //add to links list
}

复制传入的链接对象两次:在创建任何类型的包装器对象时,然后在将该包装器添加到std::list对象时再次复制。不执行任何这些包装方法都会将其限制为单个副本。有没有一种方法可以让我的蛋糕也吃,有一个必要的副本来添加,同时让Holder包装Linker对象,这样它们的引用也包含它们所在的Holder对象?或者,在完成同样的事情的同时,我是否可以为Linker使用一些更好的"寻址"方案来不符合这种范式?

最好,它是一个容易扩展的东西,所以如果我要添加这个:

struct HolderHolder
{
 std::list<Holder> holders;
};

它建立了一种类似于Linker和Holder的关系,这可以在没有真正疯狂的类型名称的情况下完成。对于HolderHolderHolder等,

您想要在对象中存储对对象的引用的最有效方法吗?

最有效的方法是使用指针。它可能是4或8个字节,并且可能不会影响结构中下一项的对齐,因此可以。

您可以通过使用查找所有者对象的数字来保存字节,但这实际上可能不会在结构分配中保存实际字节,只是引入了填充。

设置指针需要一个内存字写入。目标可能与初始化期间正在执行的其他写入相邻;即使在严格的循环中,它也不太可能影响性能。

使用stl::list的另一种选择是将列表节点本身放入数据结构中。

这在高性能环境中很常见,例如内核。以下是对Linux内核的描述。

通过在结构中放置下一个(可能还有上一个)指针(或对它们进行异或运算以节省空间),不需要单独的内存分配。

这意味着列表中的对象一次只能在一个列表中,但您的to字段无论如何都意味着这一点,因此这不会约束您。

你可以有一个约定,头部实际上是拥有的对象;这显然需要O(n)来发现,但也许你只需要偶尔发现to

总结一下:

  • 您可以通过根本不使用stl::list来节省内存,而只在节点本身中使用next(和prev,或者可能对它们进行XOR)
  • 可以使用保存的空间显式添加to字段
  • 或者你可以有一个约定,列表的头实际上是所有者