функция shrink_to_fit () удаляет нулевые указатели? - PullRequest
0 голосов
/ 13 апреля 2020

Я хотел бы спросить вас о функции vector :: shrink_to_fit ().

Допустим, у меня есть вектор указателей на объекты (или unique_ptr в моем случае), и я хочу изменить его размер на количество объектов, которые он хранит.

В какой-то момент я удаляю некоторые объекты из вектора по выбору, используя функцию release () unique_ptr, так что в этом указанном c месте в указанном месте есть нулевой указатель вектор, насколько я знаю.

Так что я хочу изменить его размер и удалить этот нулевой указатель между элементами вектора, и я спрашиваю, могу ли я сделать это с помощью функции shrink_to_fit ()?

Ответы [ 4 ]

2 голосов
/ 13 апреля 2020

Нет, shrink_to_fit не изменяет содержимое или размер вектора. Все, что он может сделать, это освободить часть своей внутренней памяти обратно в библиотеку более низкого уровня или ОС, и т. Д. c. за кулисами. Это может сделать недействительными итераторы, указатели и ссылки, но единственное другое изменение, которое вы можете увидеть, это сокращение capacity(). Это также справедливо для shrink_to_fit, абсолютно ничего не делающего.

Звучит так, будто вам нужен идиома "Erase-remove" :

vec.erase(std::remove(vec.begin(), vec.end(), nullptr), vec.end());

The std::remove сдвигает все элементы, которые не сравниваются, равными nullptr влево, заполняя «пробелы». Но это не меняет размер вектора; вместо этого он возвращает итератор в позицию в векторе сразу после последовательности смещенных элементов; остальные элементы все еще существуют, но были удалены. Затем функция-член erase избавляет от ненужных конечных элементов, уменьшая размер вектора.

Или, как отмечает @chris, C ++ 20 добавляет erase перегрузку к std::vector и связанный erase_if, что облегчает задачу. Возможно, они уже поддерживаются в MSV C 2019. Использование нового erase может выглядеть так:

vec.erase(nullptr);
0 голосов
/ 14 апреля 2020

В конце концов решение, которое я нашел, было простым:

void Controller::delete_allocated_memory(int index)
{
    m_vec.erase(m_vec.begin() + index);
    m_vec.shrink_to_fit();
}

, оно отлично работает, даже если вектор состоит из unique_ptrs, насколько я знаю, он даже не создает нулевой указатель, который я говорил о том, что он сдвигает все существующие объекты в векторе.

что вы думаете?

0 голосов
/ 13 апреля 2020

m_gates[index].release(); m_gates.shrink_to_fit();

Исходя из вашего комментария, вам нужно просто стереть этот единственный элемент с вашего вектора прямо там и сейчас. Замените оба этих утверждения на:

m_gates.erase(m_gates.begin() + index);

Или более универсальную c версию, если возможна замена контейнеров в будущем:

using std::begin;
m_gates.erase(std::next(begin(m_gates), index));

erase поддерживает итераторы, а не индексы, так что там есть преобразование. Это удалит указатель из вектора при вызове его деструктора, что заставит unique_ptr правильно очистить его память.

Теперь стирание элементов по одному может потенциально быть проблемой производительности. Если это в конечном итоге вызывает озабоченность, вы можете сделать то, что вы поняли в вопросе, и обнулить их, а затем удалить их все в одном go позже:

m_gates[index].reset();

// At some point in the program's future:
std::erase(m_gates, nullptr);

Что у вас щас очень вероятно будет утечка памяти. release освобождает владелец управляемой памяти, что означает, что теперь вы несете ответственность за ее очистку, а это не то, что вы искали. И erase, и reset (или, что эквивалентно, = {} или = nullptr) фактически вызовут деструктор unique_ptr, пока он все еще владеет, и правильно очистят память. shrink_to_fit относится к векторной емкости, а не к размеру и не имеет отношения.

0 голосов
/ 13 апреля 2020

Это быстрое тестовое шоу, которое вы не можете сделать так.

int x = 1;
vector<int*> a;
cout << a.capacity() << endl;
for (int i = 0; i < 10; ++i) {
   a.push_back(&x);
}
cout << a.capacity() << endl;
a[9] = nullptr;
a.shrink_to_fit();
cout << a.capacity() << endl;

Результат:

0
16
10
...