固定长度数组数据结构,允许快速重用任意元素? C ++

时间:2011-07-24 06:32:16

标签: c++ arrays fixed

我对c ++比较陌生,我正在尝试为特定问题选择最合适的数据结构,但我发现很难找到答案。

我希望创建一个小的(最多1000个elems)数组,包括整数或简单结构。在我的代码中,我将需要添加和删除数组中的元素,但我不希望一直动态地重新分配ram的开销。此外,因为我将有其他变量指向我的数组中的元素我不想重新编号/重新排序元素,因为这将搞砸这种关系。由于我可以确定我的数组中最大数量的元素,我很乐意预先分配所有必需的ram,但我不确定如何有效地跟踪哪些元素变为空闲,以便我可以重新使用它们作为新元素需要。这类问题有明显的数据结构吗? 提前谢谢。

8 个答案:

答案 0 :(得分:3)

std:vector<>似乎很符合您的要求:

  • 使用vector::reserve()为您为阵列规划的最大元素数量分配足够的存储空间 - 请注意reserve()实际上并未向向量添加元素。向量仍将具有与调用reserve()之前相同数量的元素。但是,它确保vector在将元素添加到vector时不需要执行重新分配,除非该附加元素会导致元素数量超过预留。这也意味着指向vector的指针将保持稳定。
  • 保证
  • 元素驻留在连续的内存块中,并使用与普通指针算法兼容的元素进行寻址。换句话说,如果你有一个指向一个元素的指针,你可以使用普通指针算法(或数组索引)获得指向另一个元素的指针

答案 1 :(得分:2)

您正在寻找Pool allocator。您可以自己编写或使用Boost.Pool

答案 2 :(得分:2)

您所描述的基本上是固定大小的内存池。你没有解释为什么需要它。除非有特定的理由将对象保持在类似数组的结构中,否则您应该通过new单独分配它们。您不需要池分配器,除非分析器说服您。

如果您确实有理由将所有对象保留在数组中,无论出于何种原因,您将实现自己的基于数组的池分配器。最简单的一个使用简单的链表来跟踪空闲块。保留第一个未使用元素的索引,每个未使用的元素保留下一个元素的索引。 除非你确切地知道自己在做什么以及为什么,否则你不应该这样做。

答案 3 :(得分:0)

  

我不希望一直动态重新分配ram的开销

您可以考虑使用vector<>。它在需要时进行动态分配,但不是所有时间。它是高效编写的容器。

  

因为我可以确定我的数组中有最大数量的元素   很高兴预先分配所有必需的

在声明矢量时,您可以将大小指定为:

vector<int> vi(1000);

您也可以从其他地方参考vi

答案 4 :(得分:0)

std::vector 是您应该使用的&amp;使用智能指针代替原始指针。

答案 5 :(得分:0)

由于您仍希望维护外部变量和内部数组元素之间的关系,是的,您无法对它们进行重新排序。但是,当您删除/添加无法满足您需求的新元素时,使用std :: vector将重新排序其余元素。

您可以将bool值与数组中的每个元素组合,指定该元素是否正在使用中。请注意,如果内存对您至关重要,则可以使用位图。

struct CElement
{
  // specify whether this element is in use
  bool isUsed;
  int element;
}

const size_t MAX_CAPACITY = 1000;
CElement myArray[MAX_CAPACITY];

另一种选择是使用链接列表。添加或删除链接列表中的节点需要恒定时间,而指向其他节点的指针仍保持不变。因此,您可以通过让外部变量保存指向节点而不是数组索引的指针来建立“关系”。此外,您甚至不需要提前预先分配1000个元素。

答案 6 :(得分:0)

我将补充一点,即分配整数/小结构的开销很小,如果你初始化一个没有元素的vector而使用vector.push_back()vector.erase(),那就意味着你不需要跟踪哪些元素是免费的,哪些元素不是。你似乎关注效率,但记得按照这个顺序做事:

  1. 实施简洁易读的解决方案。使用能够清楚表达您想要的功能。
  2. 如果且仅当您发现解决方案存在严重的性能问题,请开始使用更复杂但更高效的解决方案。

答案 7 :(得分:0)

我认为在这种情况下最好的方法是使用预先分配的双向链表......

// Untested code... just to give the idea

struct Node
{
    int data;
    Node *prev, *next;

    static Node *first, *last, *free;

    // Allocates a new node before the specified node or
    // at the end of the list if before is NULL
    static Node *alloc(int data, Node *before)
    {
        // Check the free list first
        Node *n = free;
        if (!n)
        {
            // There are no free nodes... allocate a bunch of them
            Node *page = new Node[1000];
            for (int i=0; i<999; i++)
            {
                page[i].next = &page[i+1];
            }
            page[999].next = NULL;
            n = free = &page[0];
        }

        // Update the free list
        free = n->next;

        // Link the new node to neighbors
        n->next = before;
        n->prev = before ? before->prev : last;
        if (n->prev) n->prev->next = n; else first = n;
        if (n->next) n->next->prev = n; else last = n;

        // Initialize it
        n->data = data;

        return n;
    }

    // Deallocates a node, placing it in the free list for reuse
    static dealloc(Node *n)
    {
        if (n)
        {
            // Remove from double chain
            if (n->next) n->next->prev = n->prev; else last = n->prev;
            if (n->prev) n->prev->next = n->next; else first = n->next;

            // Add to free list
            n->next = free; free = n;
        }
    }
};

当您需要分配节点时,只需调用Node::alloc传递数据以及放置节点的位置。当你需要释放它时,只需调用node dealloc。

节点在“页面”中分配,并在重新分配后重用。这样可以最大限度地减少对内存管理器的调用次数,并且可以大大加快速度。

双向链接结构永远不需要在内存中移动现有节点(因此不需要调整指针)。重新排序元素也很容易,例如添加Node::moveTo(Node *before)方法。

这种方法的缺点是,在给定索引的情况下访问第n个元素是O(n)操作。