структура данных массива фиксированной длины, которая позволяет быстро использовать произвольные элементы? C ++ - PullRequest
4 голосов
/ 24 июля 2011

Я относительно новичок в c ++ и пытаюсь выбрать наиболее подходящую структуру данных для конкретной проблемы, но мне трудно найти ответы.

Я хочу создать небольшой (не более 1000 элементов) массивиз целых или простых структур.В любое время в моем коде мне нужно будет добавлять и удалять элементы из моего массива, но я не хочу, чтобы все время динамически перераспределялись оперативные памяти.Также, так как у меня будут другие переменные, которые указывают на элементы в моем массиве, я не хочу перенумеровать / переупорядочивать элементы, так как это испортит эту связь.Так как я могу быть уверен в максимальном количестве элементов в моем массиве, я рад предварительно выделить все необходимые оперативные памяти, но я не уверен, как эффективно отслеживать, какие элементы становятся свободными, чтобы я мог повторно использовать их для новых элементов, какнеобходимо.Существует ли очевидная структура данных для этого типа проблемы?Заранее спасибо.

Ответы [ 8 ]

3 голосов
/ 24 июля 2011

A std:vector<>, кажется, вполне соответствует вашим требованиям:

  • используйте vector::reserve(), чтобы выделить достаточно памяти для максимального количества элементов, которые вы планируете иметь в массиве - обратите внимание, что reserve() на самом деле не добавляет элементы в вектор.Вектор будет по-прежнему иметь то же количество элементов, что и до вызова reserve().Однако это гарантирует, что vector не потребуется выполнять перераспределение при добавлении элемента в vector, если только этот дополнительный элемент не приведет к тому, что количество элементов превысит резервирование.Это также означает, что указатели на vector будут оставаться стабильными. * Элементы 1011 *
  • гарантированно будут находиться в непрерывном блоке памяти с адресацией элементов, совместимых с обычной арифметикой указателей.Другими словами, если у вас есть указатель на один элемент, вы можете получить указатель на другой элемент, используя обычную арифметику указателя (или индексацию массива)
2 голосов
/ 24 июля 2011

То, что вы описываете, это, по сути, пул памяти фиксированного размера.Вы не объяснили, зачем вам это нужно.Если нет особой причины держать ваши объекты в массивоподобной структуре, вы должны просто распределить их по отдельности через new.Вам не нужен распределитель пула, если только профилировщик не убедит вас в обратном.

Если у вас есть причина сохранить все ваши объекты в массиве, какой бы ни была эта причина, вы собираетесь реализовать свой собственный массив.на основе распределителя пула.Самый простой из них использует простой связанный список для отслеживания свободных фрагментов.Вы сохраняете индекс первого неиспользуемого элемента, а каждый неиспользуемый элемент сохраняет индекс следующего. Вы не должны этого делать, если не знаете точно, что делаете и почему .

2 голосов
/ 24 июля 2011

Вы ищете Распределитель пула .Вы можете написать свой собственный или использовать Boost.Pool

0 голосов
/ 24 июля 2011

Я думаю, что лучшим подходом в этом случае является использование предварительно выделенного двусвязного списка ...

// 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, передавая данные и куда поместить узел. Когда вам нужно освободить его, просто вызовите узел dealloc.

Узлы размещаются в «страницах» и повторно используются после освобождения. Это сводит к минимуму количество обращений к диспетчеру памяти и может значительно ускорить процесс.

Двухсвязной структуре никогда не потребуется перемещать существующий узел, пока он находится в памяти (поэтому указатели не нужно настраивать). Также легко изменить порядок элементов, например, добавив метод Node::moveTo(Node *before).

Недостаток этого подхода заключается в том, что для доступа к n-му элементу, заданному индексу, требуется операция O (n).

0 голосов
/ 24 июля 2011

Я добавлю, что накладные расходы на распределение целых / малых структур малы, и что если вы инициализируете vector без элементов и используете vector.push_back() и vector.erase(), это будет означать, что вам не нужноотслеживать, какие элементы свободны, а какие нет.Вы, кажется, обеспокоены эффективностью, но не забывайте делать вещи в такой последовательности:

  1. Реализуйте чистое и легко читаемое решение.Используйте функции, которые четко выражают то, что вы хотите.
  2. Если и только если вы обнаружите, что решение страдает от значительных проблем с производительностью, начните использовать более сложные, но более эффективные решения.
0 голосов
/ 24 июля 2011

Поскольку вы все еще хотите поддерживать связь между внешними переменными и внутренними элементами массива, да, вы не можете изменить их порядок. Однако использование 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 элементов заранее.

0 голосов
/ 24 июля 2011

std :: vector - это то, что вы должны использовать и использовать Умные указатели вместо необработанных указателей.

0 голосов
/ 24 июля 2011

Я не хочу все время динамически перераспределять оперативную память

Вы можете подумать об использовании vector<>. Он выполняет динамическое распределение при необходимости, но не все время. Это эффективно написанный контейнер.

Поскольку я могу быть уверен в максимальном количестве элементов в моем массиве, я рад заранее выделить все необходимое

При объявлении вектора вы можете указать его размер:

vector<int> vi(1000);

Вы также можете обратиться к vi из других мест.

...