#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. Я думаю, что существует поддержка распределителя с сохранением состояния, если операторы == не всегда сравниваются одинаково. Но я все равно рекомендую отойти от стека