Как очистить вектор с
Перейти к содержимому

Как очистить вектор с

  • автор:

Удалить элемент из vector и очистить память

При удалении элемента из вектора, хочу очистить занимаемую элементом Anchor память, но возникает Исключение.

Отслеживать
29.6k 3 3 золотых знака 17 17 серебряных знаков 36 36 бронзовых знаков
задан 17 мая 2022 в 6:43
1,925 11 11 серебряных знаков 21 21 бронзовый знак

С помощью оператора delete следует удалять только объекты, с динамическим временем хранения созданные с помощью оператора new . На строке delete &a; удаляется объект с автоматическим временем хранения, созданный на три строки выше в этой же функции.

17 мая 2022 в 6:49
Зачем изобретать велосипед? Вектор STL может и в конец добавлять и удалять определённые элементы.
17 мая 2022 в 7:24

@bloody, причем тут вектор? Распределитель вектора может освобождать память, которую выделял для элемента вектора, Получается, что элемент мы создали в одном участке памяти, а его вектор занес в другой.

17 мая 2022 в 9:40

Потеряна информация где был создан элемент изначала. С таким кодом уже ничего не сделаешь _ утечка памяти неизбежна, и надежда только на уборщика мусора. Память освобождать нужно в самих функциях_ после push_back

17 мая 2022 в 9:42

Если я в vector храню указатели на структуры, которые я создал через new. и потом хочу удалить элемент из вектора (указатель на структуру), то нужно ли мне очищать саму память занятую структурой? ведь из вектора просто пропадет указатель, а память не будет очищена. P.S. я переписал частично код и храню указатели vector Anchors;

17 мая 2022 в 9:54

2 ответа 2

Сортировка: Сброс на вариант по умолчанию

Вектор, как и все стандартные контейнеры, хранит копии того, что вы в него добавляете.

Anchor* a = new Anchor(addr); p->Anchors.push_back(*a); 

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

В этом же причина краша — вы зовете delete не на том объекте, который создали в куче, а на его копии, которую создал вектор.

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

Правильно добавлять элемент так: .emplace_back(addr); или .push_back(Anchor(addr)); (для вашего класса .push_back(addr); тоже сработает). При удалении delete не нужен, достаточно erase .

Link тоже нет смысла создавать через new . Просто Link l; . return l; . И еще, раз вы пишете на С++ а не на С, то вместо struct Link достаточно писать просто Link (везде кроме самого определения структуры).

Как очистить вектор с

Для добавления элементов в вектор применяется функция push_back() , в которую передается добавляемый элемент:

#include #include int main() < std::vectornumbers; // пустой вектор numbers.push_back(5); numbers.push_back(3); numbers.push_back(10); for(int n : numbers) cout << n << "\t"; // 5 3 10 std::cout

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

Функция emplace_back() выполняет аналогичную задачу — добавляет элемент в конец контейнера:

std::vector numbers< 1, 2, 3, 4, 5 >; numbers.emplace_back(8); // numbers = < 1, 2, 3, 4, 5, 8 >;

Добавление элементов на определенную позицию

Ряд функций позволяет добавлять элементы на определенную позицию.

  • emplace(pos, value) : вставляет элемент value на позицию, на которую указывает итератор pos
  • insert(pos, value) : вставляет элемент value на позицию, на которую указывает итератор pos, аналогично функции emplace
  • insert(pos, n, value) : вставляет n элементов value начиная с позиции, на которую указывает итератор pos
  • insert(pos, begin, end) : вставляет начиная с позиции, на которую указывает итератор pos, элементы из другого контейнера из диапазона между итераторами begin и end
  • insert(pos, values) : вставляет список значений начиная с позиции, на которую указывает итератор pos
std::vector numbers< 1, 2, 3, 4, 5 >; auto iter = numbers.cbegin(); // константный итератор указывает на первый элемент numbers.emplace(iter + 2, 8); // добавляем после второго элемента numbers = < 1, 2, 8, 3, 4, 5>;
std::vector numbers1< 1, 2, 3, 4, 5 >; auto iter1 = numbers1.cbegin(); // константный итератор указывает на первый элемент numbers1.insert(iter1 + 2, 8); // добавляем после второго элемента //numbers1 = < 1, 2, 8, 3, 4, 5>; std::vector numbers2 < 1, 2, 3, 4, 5 >; auto iter2 = numbers2.cbegin(); // константный итератор указывает на первый элемент numbers2.insert(iter2 + 1, 3, 4); // добавляем после первого элемента три четверки //numbers2 = < 1, 4, 4, 4, 2, 3, 4, 5>; std::vector values < 10, 20, 30, 40, 50 >; std::vector numbers3 < 1, 2, 3, 4, 5 >; auto iter3 = numbers3.cbegin(); // константный итератор указывает на первый элемент // добавляем после первого элемента три первых элемента из вектора values numbers3.insert(iter3 + 1, values.begin(), values.begin() + 3); //numbers3 = < 1, 10, 20, 30, 2, 3, 4, 5>; std::vector numbers4 < 1, 2, 3, 4, 5 >; auto iter4 = numbers4.cend(); // константный итератор указывает на позицию за последним элементом // добавляем в конец вектора numbers4 элементы из списка < 21, 22, 23 >numbers4.insert(iter4, < 21, 22, 23 >); //numbers4 = < 1, 2, 3, 4, 5, 21, 22, 23>;

Удаление элементов

Если необходимо удалить все элементы вектора, то можно использовать функцию clear :

std::vector v < 1,2,3,4 >; v.clear();

Функция pop_back() удаляет последний элемент вектора:

std::vector v < 1,2,3,4 >; v.pop_back(); // v =

Если нужно удалить элемент из середины или начала контейнера, применяется функция std::erase() , которая имеет следующие формы:

  • erase(p) : удаляет элемент, на который указывает итератор p. Возвращает итератор на элемент, следующий после удаленного, или на конец контейнера, если удален последний элемент
  • erase(begin, end) : удаляет элементы из диапазона, на начало и конец которого указывают итераторы begin и end. Возвращает итератор на элемент, следующий после последнего удаленного, или на конец контейнера, если удален последний элемент

std::vector numbers1 < 1, 2, 3, 4, 5, 6 >; auto iter = numbers1.cbegin(); // указатель на первый элемент numbers1.erase(iter + 2); // удаляем третий элемент // numbers1 = < 1, 2, 4, 5, 6 >std::vector numbers2 = < 1, 2, 3, 4, 5, 6 >; auto begin = numbers2.cbegin(); // указатель на первый элемент auto end = numbers2.cend(); // указатель на последний элемент numbers2.erase(begin + 2, end — 1); // удаляем с третьего элемента до последнего // numbers2 =

Также начиная со стандарта С++20 в язык была добавлена функция std::erase() . Она не является частью типа vector. В качестве первого параметра она принимает вектор, а в качестве второго — элемент, который надо удалить:

std::vector numbers3 < 1, 2, 3, 1, 5, 6 >; std::erase(numbers3, 1); // numbers3 =

В данном случае удаляем из вектора numbers3 все вхождения числа 1.

Размер вектора

С помощью функции size() можно узнать размер вектора, а с помощью функции empty() проверить, путой ли вектор:

#include #include int main() < std::vectornumbers; if(numbers.empty()) std::cout

С помощью функции resize() можно изменить размер вектора. Эта функция имеет две формы:

  • resize(n) : оставляет в векторе n первых элементов. Если вектор содержит больше элементов, то его размер усекается до n элементов. Если размер вектора меньше n, то добавляются недостающие элементы и инициализируются значением по умолчанию
  • resize(n, value) : также оставляет в векторе n первых элементов. Если размер вектора меньше n, то добавляются недостающие элементы со значением value

std::vector numbers1 < 1, 2, 3, 4, 5, 6 >; numbers1.resize(4); // оставляем первые четыре элемента — numbers1 = numbers1.resize(6, 8); // numbers1 =

Важно учитывать, что применение функции resize может сделать некорректными все итераторы, указатели и ссылки на элементы.

Изменение элементов вектора

Функция assign() позволяет заменить все элементы вектора определенным набором:

std::vector langs = < "Java", "JavaScript", "C">; langs.assign(4, «C++»); // langs =

В данном случае элементы вектора заменяются набором из четырех строк «C++».

Также можно передать непосредственно набор значений, который заменит значения вектора:

std::vector langs< "Java", "JavaScript", "C">; langs.assign(< "C++", "C#", "C">); // langs =

Еще одна функция — swap() обменивает значения двух контейнеров:

std::vector clangs < "C++", "C#", "Java" >; std::vector ilangs < "JavaScript", "Python", "PHP">; clangs.swap(ilangs); // clangs = < "JavaScript", "Python", "PHP">; for(std::string lang : clangs)

Сравнение векторов

Векторы можно сравнивать — они поддерживают все операции сравнения: , =, ==, !=. Сравнение контейнеров осуществляется на основании сравнения пар элементов на тех же позициях. Векторы равны, если они содержат одинаковые элементы на тех же позициях. Иначе они не равны:

std::vector v1 ; std::vector v2 ; std::vector v3 ; bool v1v2 = v1 == v2; // true bool v1v3 = v1 != v3; // true bool v2v3 = v2 == v3; // false

C++: Очистка std::vector

Уважаемые программисты! Допустим, создается и заполняется vector:

vector float> items; . float *x; for( int i = 0; i100; i++) < x = new float; *x = 1.0f; items.push_back( *x); >;

При этом создаваться могут экземпляры struct и классов, поэтому в вектор записываются ссылки.

Если необходимо очистить динамическую память, достаточно

items.clear( );
float *x; if ( items.size( ) > 0)< for( int i = 0; iitems.size( ); i++) < *x = items[i]; SAFE_DELETE( x); >; >; items.clear( );

(второй вариант более логичен, но возникает run-time ошибка)?

  • return []()<>;
  • Участник

#1
10:46, 25 сен 2014

Странствуй 8 летучи с++, ты пихаешь объекты в вектор по значению, память утекает.

  • Walter Sullivan
  • Постоялец

#2
10:47, 25 сен 2014

Здесь всё плохо.
У вас утечка памяти.

items.clear() просто удаляет все элементы вектора, не освобождает никакую память.

  • Kartonagnick
  • Постоялец

#3
10:58, 25 сен 2014

float *x; // for( int i = 0; i100; i++) < x = new float; // *x = 1.0f; items.push_back( *x); >;

Предыдущий адрес теряется. Память по нему уже не освободить. Утечка.

#4
11:02, 25 сен 2014

vector float*> items; . float *x; for( int i = 0; i100; i++) < x = new float; *x = 1.0f; items.push_back( x); > if ( items.size( ) > 0) < for( int i = 0; i  items.size( ); i++) < *x = items[i]; SAFE_DELETE( x); > > items.clear( );

float’ы конечно не разумно хранить таким образом, но ошибку свою надеюсь ты поймешь. 🙂

#5
11:17, 25 сен 2014

Изображение

old_proger
facepalm

  • Kartonagnick
  • Постоялец

#6
11:23, 25 сен 2014

old_proger
Вообще, ваш код можно переписать без потери смысла вот так:

vector float> items; . for( int i = 0; i100; i++) items.emplae_back( 1.0f); // // нужное вам значение сразу же запихивайте в вектор // рекомендую использовать emplace_back вместо push_back //для тяжелых объектов это работает быстрее поскольку позволяет избежать копирования . items.clear( ); // // так же, если закончится время жизни вектора, он самостоятельно позаботится об очистке памяти за собой

Суть использования высокоуровневых механизмов таких, как контейнеры, смарт-поинтеры и прочее — в том и заключается,
что вам больше не нужно вручную выделять память, и самостоятельно следить за ней.

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

#7
11:29, 25 сен 2014

old_proger
> При этом создаваться могут экземпляры struct и классов, поэтому в вектор
> записываются ссылки.

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

#8
11:53, 25 сен 2014

0iStalker
> В вектор запихивай или значения или смартпоинтеры. а за ручное выделение
> памяти в С++, без важной на то причины, надо руки отрывать.
Какие смарт поинтеры? За использование shared_ptr без уважительной причины, нужно не то что руки отрывать, от компа отлучать на не определенное время.

Алёна C++

Некоторое время назад я писала про выделение памяти под vector и в конце немного затронула тему высвобождения памяти. В большинстве реализаций освободить память, выделенную под vector, можно только с помощью трюка, известного как swap trick. Недавно я вычитала, что в реализации STL из Visual C++ 7.1 память, выделенная под вектор, высвобождается при вызове метода clear(). 7.1 у меня нет, зато у меня есть Microsoft Visual C++ Toolkit 2003. Действительно, освобождается. Для такого кода:

vector v;
v.reserve(17);
coutv.clear();
cout
Вывод получается такой:

Microsoft Visual C++ Toolkit 2003:
17
0 //действительно, освободилась
MSVC++6.0:
17
17
MinGW gcc 3.4.4:
17
17

Я решила копать дальше. Я всегда считала, что метод clear для последовательных контейнеров эквивалентен erase всего контейнера. Нашла упоминание об этом в документации STL на sgi.com. Вот оттуда выдержка:
a.clear() Equivalent to a.erase(a.begin(), a.end())
Запускаю код:
vector v;
v.reserve(17);
coutv.erase(v.begin(), v.end());
cout
Получаю:
Microsoft Visual C++ Toolkit 2003:
17
17 //не освободилась
MSVC++6.0:
17
17
MinGW gcc 3.4.4:
17
17

Получается, что в случае вышеупомянутого тулкита нет обещанной эквивалентности. Так, а что говорит об этом Стандарт? Вот тут интересный момент. Там нет слова «эквивалентно». Там erase(begin(), end()) приписано к clear() в качестве assertion/note для последовательных контейнеров. А вот требование там одно, что post condition: size()==0. Оно тут выполняется.
Так что MSVC++2003 тут прав.
Я пробовала также v.resize(0). Пробовала удалить все элементы вектора с помощью pop_back’ов. Память не освобождается. Это происходит только при вызове clear.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *