Как вы получаете std ::list для выделения памяти для n объектов в C ?

#c #list #std

#c #Список #std

Вопрос:

Моя цель — иметь std ::list, который выделяет достаточно памяти для объектов, которые я в него помещу, поэтому мне не нужно иметь дело с потенциальными исключениями при его расширении или дополнительным временем, необходимым для его расширения.

Моя первая попытка включает в себя сращивание из волновой таблицы:

 std::list<T> list();
        auto listI = list.begin;
        typename std::list<T>::iterator waveStart = waveTable.begin();
        for(int i = 0; i < waveIndex; i  ) {
            waveStart  ;
        }
        typename std::list<T>::iterator waveEnd;
        int tCounter = nSamples;
        while(tCounter > 0) {
            if(tCounter > (waveTable.size() - waveIndex)) {
                waveEnd = waveStart;
                for(int i = 0; i < (waveTable.size() - waveIndex); i  ) {
                    waveEnd  ;
                }
                tCounter = (tCounter - (waveTable.size() - waveIndex));
            } else {
                waveEnd = waveStart;
                for(int i = 0; i < tCounter; i  ) {
                    waveEnd  ;
                }
                tCounter = 0;
            }
            list.splice(listI, waveTable, waveStart, waveEnd);
            waveStart = waveTable.begin();
        }

        phase  = (tp * frequency * (nSamples/sampleRate));
        while(phase > tp) {
            phase -= tp;
        }
        waveIndex = (phase / tp) * waveTable.size();
 

Я планировал скопировать значения, но splice удалил значения из waveTable, поэтому я собираюсь использовать insert .

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

Ответ №1:

Моя цель — иметь std ::list, который выделяет достаточно памяти для объектов, которые я в него помещу

Хорошо, кажется разумным. Вы можете сделать это, вызвав соответствующий конструктор, как упоминал @Gyross.

поэтому мне не приходится иметь дело с потенциальными исключениями при его расширении

Теперь ваш вопрос перестает иметь смысл. Если у вас не хватает памяти, предварительное выделение, конечно, ничего не исправит. Также, в зависимости от вашего типа, он все равно может потенциально вызывать исключения, когда вы вызываете функцию присваивания копирования (или, в некоторых случаях, перемещения) (при использовании node = newvalue ).

или дополнительное время, необходимое для его расширения

Иронично, что вы используете std::list и так сильно заботитесь о производительности, поскольку это один из, если не самый медленный контейнер в стандартной библиотеке. Если вам нужен высокопроизводительный фиксированный контейнер, я рекомендую написать / использовать кольцевой буфер.

Ответ №2:

Вы можете указать начальный размер в конструкторе с std::list<T> list_obj(n) помощью .

https://en.cppreference.com/w/cpp/container/list/list

Есть еще std::list::resize кое — что .

Комментарии:

1. Но затем вставка увеличивает размер

2. вставка всегда будет увеличивать размер, вместо того, чтобы делать list.insert(it, val) , делать *it = val .

Ответ №3:

Возможно, вы захотите рассмотреть возможность реализации пользовательского распределителя.

Полные требования к пользовательскому классу распределителя здесь https://en.cppreference.com/w/cpp/memory/allocator

Приведенный ниже пример распределителя всегда выделяет num_items элементы в стеке и выбрасывает std::bad_alloc() , если количество элементов превышает статически определенный размер.

Возможно, вы захотите отойти от стека, используя new delete или что-то подобное, чтобы избежать переполнения, если у вас много элементов.

 template<typename T, size_t num_items>
class MyAllocator
{
public:
    using value_type = T;
    using pointer = T*;
    using const_pointer = const T*;
    using void_pointer = std::nullptr_t;
    using const_void_pointer = const std::nullptr_t;
    using reference = Tamp;;
    using const_reference = const Tamp;;
    using size_type = std::size_t;
    using difference_type = std::ptrdiff_t;

    /* should copy assign the allocator when copy assigning container */
    using propagate_on_container_copy_assignment = std::true_type;

    /* should move assign the allocator when move assigning container */
    using propagate_on_container_move_assignment = std::true_type;

    /* should swap the allocator when swapping the container */
    using propagate_on_container_swap = std::true_type;

    /* two allocators does not always compare equal */
    using is_always_equal = std::false_type;

    MyAllocator() : m_index(0) {};
    ~MyAllocator() noexcept = default;

    template<typename U>
    struct rebind {
        using other = MyAllocator<U, num_items>;
    };

    [[nodiscard]] pointer allocate(size_type n) {
        if (m_index n >= num_items)
            throw std::bad_alloc();

        pointer ret = amp;m_buffer[m_index];
        m_index  = n;
        return ret;

    }

    void deallocate(pointer p, size_type n) {
        (void) p;
        (void) n;
        /* do nothing */
    }

    size_type max_size() {
        return num_items;
    }


    bool operator==(const MyAllocatoramp; other) noexcept {
        /* storage allocated by one allocator cannot be freed by another */
        return false;
    }

    bool operator!=(const MyAllocatoramp; other) noexcept {
        /* storage allocated by one allocator cannot be freed by another */
        return !(*this == other);
    }

private:
    value_type m_buffer[num_items];
    size_t m_index;
};

 

И тогда вы можете использовать

 constexpr size_t always_allocate_this_amount_of_elements = 10;
std::list<int, MyAllocator<int, always_allocate_this_amount_of_elements>> list;
 

Комментарии:

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

2. Я думаю, что существует поддержка распределителя с сохранением состояния, если операторы == не всегда сравниваются одинаково. Но я все равно рекомендую отойти от стека