Вектора в С++ часть 5 (функции insert(), swap(), shrink_to_fit())
Вектора, позволяют помещать число в любую ячейку вектора (даже если эта ячейка уже занята), пример для понимания:
#include #include using namespace std; int main() < vector vecInt(3,100); ///Создаем вектор из 3 элементов и заполняем его значением 100 vector ::iterator it; it = vecInt.begin(); ///Итератор указывает на vec[0] ///Вектор расширяется теперь до 4 элементов vecInt.insert (it,200); ///И первым элементом записывается 200 ///Вектор расширяется теперь до 5 элементов it = vecInt.begin() + 3;///Вектор указывает на 4 элемент (0-элемент+3-элемента) vecInt.insert(it,300);///И четвертым элементом записывается 300 vecInt.insert(it+1,900);///Вектор расширяется теперь до 6 элементов и 5 элементом записывается 900 cout cout
Работа нашей программы:
CppStudio.com
Vector contains: 200 100 100 300 900 100
Vector max_size: 1073741823
Vector size: 6 New vector size: 10
В 6 строке объявлен вектор размером из трех элементов типа int и сразу же заполнили его числом 100. В строке 7 мы объявляем итератор it , в 8 строке наш итератор указывает на нулевой элемент нашего вектора. В 11 строке мы встречаем новую функцию vector::insert() .
Функция vector::insert() принимает два аргумента (первый – указатель на ячейку вектора, куда будет помещено число, второй – само помещаемое число). Так как итератор it указывает на vecInt[0] , нулевой элемент вектора, то теперь vecInt[0]=200 . Длина вектора соответственно увеличивается на один элемент. В строке 14 итератор it теперь указывает на третий элемент вектора, т. е. vecInt[3] , соответственно назначение строки 15 становиться уже понятно.
В строке 16 число 900 мы записываем в позицию it+1 или vecInt[3+1] . В строках с 19-22 осуществляется вывод вектора, но это должно быть уже Вам понятно. В строке 24 используется функция vector::max_size() , которая выводит максимальный размер вектора, т. е. максимально возможное количество, элементов которое может вместить в себя вектор. В строке 26 встречается новая функция vector::resize () , функция позволяет увеличить размер вектора до заданной величины, в данном случае вектор расширяется до 10 элементов.
Если функция vector::resize() позволяет уменьшить размер вектора, то функция vector::reserve( ) имеет обратное действие, она позволяет расширить вектор до заданной величины, рассмотрим пример:
#include #include using namespace std; int main() < vector vecFirst ,vecSecond; /// Создаем два вектора for ( int i=0; i cout << "vecFirst contains: "; for (int i=0; i
Insert c vector как работает
Для добавления элементов в вектор применяется функция 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
Как работает метод insert в в классе Vector в с++?
В методе insert класса Vector нужно передать итератор указывающий на позицию в которую ты хочешь поместить элемент, я правильно понимаю что для того чтобы поместить тот или иной элемент в заданную позицию нужно итерироваться через весь список пока не найдется нужная позиция ?
Отслеживать
задан 16 янв 2019 в 20:10
cvxbcvbsd fsddfgdfg cvxbcvbsd fsddfgdfg
125 12 12 бронзовых знаков
Если вы ищете конкретный элемент в векторе, чтобы в его место поместить новый, то да, нужно итерироваться по списку, чтобы найти эту позицию.
16 янв 2019 в 20:56
AR Hovsepyan, нет я имею ввиду к примеру у меня есть вектор из 10 элементов и я решил вставить элемент со значением 1 в середину вектора таким образом: vector.insert(vector.begin()+5,1).
16 янв 2019 в 21:10
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
Класс vector представляет собой динамический массив и реализует итераторы с произвольным доступом. Соответственно доступ к любой позиции всегда производится за одну операцию. Другое дело, что для того, чтобы вставить новый элемент на требуемую позицию, может потребоваться передвинуть элементы, стоящие справа от него, а то и перевыделить новый массив, если в старом не хватает места.
Отслеживать
ответ дан 16 янв 2019 в 20:20
user7860670 user7860670
29.7k 3 3 золотых знака 17 17 серебряных знаков 36 36 бронзовых знаков
зачем тогда в методе добавлять ссылку на итератор можно же просто позицию обозначить?
16 янв 2019 в 21:18
@cvxbcvbsdfsddfgdfg потому что в стандартной библиотеке много контейнеров, и у разных контейнеров итераторы с разными свойствами. Но, в зависимости от типа итератора у них есть и общие свойства. Поэтому если вы используете итераторы, вы можете писать более обобщенные алгоритмы, которые могут работать с разными контейнерами, а не только с например с вектором.
16 янв 2019 в 21:31
@cvxbcvbsdfsddfgdfg Это хороший вопрос. Многих методов на практике действительно явно не хватает.
16 янв 2019 в 21:32
@VTT интерфейс не стоит загромождать дублирующейся функциональностью без необходимости. В std::string была необходимость - обеспечить обратную совместимость.
16 янв 2019 в 21:35
@Slava Я не вижу никаких причин этого не сделать. Зачем заставлять людей каждый раз писать vector.begin() + index ? Полезных на практике перегрузок и функций жестко не хватает (это я не только вектор имею ввиду).
16 янв 2019 в 21:48
зачем тогда в методе добавлять ссылку на итератор можно же просто позицию обозначить?
Ответ на этот вопрос проще дать на примере. Например я хочу имплементировать функцию, которая вставляет элемент в n-ю позицию:
template void insert_into_pos( Container &c, size_t pos, T obj ) < if( pos >= c.size() ) throw std::runtime_error( "invalid position" ); c.insert( std::next( c.begin(), pos ), std::move( obj ) ); >
Эта функция будет работать с многими контейнерами, такими как std::vector std::list std::deque и пр. Если бы в векторе был доступен только метод с индексом, такая функция бы не работала с многими другими контейнерами.
Отслеживать
ответ дан 16 янв 2019 в 21:49
699 4 4 серебряных знака 11 11 бронзовых знаков
Тут идет важное предусловие - код должен работать со многими контейнерами - которое позволяет не касаться неудобной ситуации, когда код должен работать только с векторами. Такой пример демонстрирует почему есть методы для работы и итераторами, но ничего не поясняет почему нет методов для работы с индексами.
16 янв 2019 в 21:58
@VTT потому что через метод с итераторами легко имплементируется метод с индексами и разработчики библиотеки не видели необходимости перегружать интерфейс.
16 янв 2019 в 22:06
Такое объяснения выглядит крайне малоубедительно. не видели необходимости - как же они могли не видеть необходимости, когда использование индексов является типичным сценарием при работе с вектором? перегружать интерфейс - что вообще значит "перегружать"? и если они так боялись не "перегрузить интерфейс", то почему в нем оказались такой бессмысленный метод как max_size или те же cbegin / cend ? ведь через метод с begin легло имплементируется метод с const begin . Ведь легко писать std::as_const(c).begin() раз легло std::next(c.begin(), pos) ? Или *c.begin() вместо c.front()
16 янв 2019 в 22:43
@VTT "Ведь легко писать std::as_const(c).begin()" вообще-то std::as_const появилась в C++17, а std::vector::cbegin() в С++11. Поэтому легко нельзя. Насчет как они могли не видеть, я не знаю, я стандартную библиотеку не разабатывал, ко мне притензии предъявлять смысла нет, я просто сообщаю по факту.
vector::insert в C++ (с примерами)
Привет! Сегодня мы рассмотрим полезную функцию C++ связанную с векторами — vector::insert . Начнем с обзора, перейдем к правильному использованию, продемонстрируем примеры и закончим простой собственной реализацией для лучшего понимания этой функции.
Что такое vector::insert?
В C++, векторы являются частью Standard Template Library (STL) и действуют как динамические массивы. Вы можете добавлять элементы, удалять их и даже изменять их размер по своему усмотрению. Одной из ключевых операций для векторов является вставка элементов, и вот здесь vector::insert приходит на помощь.
Функция vector::insert позволяет добавить элемент или диапазон элементов на любую конкретную позицию в векторе, сдвигая существующие элементы.
Вот простой пример:
#include #include using namespace std; int main() vectorint> vec = 10, 20, 30>; vec.insert(vec.begin() + 1, 15); // Вставляет 15 между 10 и 20 for(int num : vec) cout <" "; > return 0; >
Запустив приведенный выше код, вы получите:
10 15 20 30
Функция vector::insert имеет несколько перегрузок. Давайте рассмотрим их подробнее.
1. Вставка одного элемента
iterator insert (iterator position, const value_type& val);
Здесь вы можете вставить одно значение val перед позицией, на которую указывает итератор.
2. Повторяющаяся вставка элементов
iterator insert (iterator position, size_type n, const value_type& val);
С помощью этой перегрузки вы можете вставить значение val n раз на указанную позицию.
vectorint> vec = 10, 20, 30>; vec.insert(vec.begin() + 1, 3, 15); // Вставляет три 15 между 10 и 20
10 15 15 15 20 30
3. Вставка из другого контейнера
iterator insert (iterator position, InputIterator first, InputIterator last);
Эта перегрузка позволяет вставить диапазон элементов из другого контейнера в вектор на указанной позиции.
4. Вставка с использованием списка инициализации
iterator insert (iterator position, initializer_listvalue_type> il);
С помощью этой перегрузки вы можете напрямую использовать список инициализации для одновременной вставки нескольких элементов.
vectorint> vec = 10, 20, 30>; vec.insert(vec.begin() + 2, 40, 50, 60>); // Вставляет 40, 50, 60 перед 30
10 20 40 50 60 30
Как это работает внутри
Когда вы используете vector::insert , это может показаться простым, но за кулисами происходит многое. Давайте рассмотрим поближе:
- Проверка емкости: Сначала функция проверяет, достаточно ли места в векторе для размещения новых элементов. Если нет, происходит перевыделение памяти, что может сделать недействительными все итераторы, указывающие на вектор.
- Сдвиг элементов: Все элементы после точки вставки сдвигаются, чтобы освободить место для новых элементов.
- Вставка элементов: Новые элементы вставляются в освободившиеся пространство.
Важно знать, что использование vector::insert может повлиять на производительность, особенно если вы вставляете на позицию, отличную от конца. Это связано с необходимостью перемещения всех последующих элементов. Поэтому используйте его с осторожностью!
Собственная реализация vector::insert
Для лучшего понимания концепции давайте создадим упрощенную версию vector::insert :
#include #include using namespace std; template typename T> void customInsert(vectorT> &vec, int pos, T value) // 1. Проверка допустимости позиции if (pos 0 || pos > vec.size()) return; // Недопустимая позиция > // 2. Добавление элемента в конец vec.push_back(value); // 3. Перемещение элементов, начиная с конца и до указанной позиции for (int i = vec.size() - 1; i > pos; --i) swap(vec[i], vec[i - 1]); > > int main() vectorint> vec = 10, 20, 30>; customInsert(vec, 1, 15); for (int num : vec) cout <" "; > return 0; >
10 15 20 30
Этот пример показывает то, что происходит при вызове vector::insert . Однако настоящая реализация STL гораздо сложнее и оптимизирована гораздо лучше.
vector::insert — это мощный инструмент для работы с векторами в C++. Он предлагает гибкость вставки элементов на любую позицию в векторе. Надеемся, что эта статья помогла вам лучше понять эту функцию и научиться правильно ее использовать.
- Базовое использование vector::insert :
- Напишите программу на C++, создающую вектор строк.
- Запросите у пользователя строку и позицию, на которой он хочет вставить эту строку.
- Используйте функцию vector::insert , чтобы добавить строку на указанную позицию.
- Покажите пользователю измененный вектор.
- Изучение перегрузок:
- Доработайте предыдущую программу, предоставив пользователю несколько вариантов действий: а. Вставить одну строку на указанную позицию. б. Вставить строку несколько раз на определенную позицию. в. Вставить диапазон строк из другого вектора на выбранную позицию. г. Вставить, используя список инициализации.
- Запросите у пользователя, какой из вариантов он хочет выбрать, и выполните соответствующее действие. Показывайте результирующий вектор после каждого изменения.
- Влияние на производительность:
- Напишите программу на C++, демонстрирующую разницу в производительности при вставке элемента в начало и в конец большого вектора.
- Создайте вектор размером 100 000, заполненный случайными числами.
- Измерьте время, необходимое для вставки нового числа в начало и в конец вектора.
- Покажите пользователю время, потраченное на каждую операцию. Размышляйте о причинах разницы во времени.
Читайте также
vector::size в C++
В C++, контейнер std::vector представляет собой динамический массив, который может автоматически изменять свой размер. Часто возникает необходимость определить, сколько элементов на данный момент содержит vector. Встречайте функцию size! В этой статье мы рассмотрим, как работает функция size, покажем практические примеры и расскажем о других полезных функциях.
Функция copy в C++
В этом уроке вы узнаете что такое функция copy, как ее использовать. А также узнаете с какими контейнерами она работает.
Итераторы в C++
Здесь вы узнаете про итераторы все, что вам нужно: что это такое, как оно работает, с какими контейнерами можно его применять. И, конечно, исключения.
Список list в C++
В этой статье мы разберемся со списками в C++. Мы узнаем что такое list, как его создать, как им пользоваться и подведем итоги.
Функция sort и компаратор в C++
Что такое сортировка, как ею управлять - все ответы находятся в этой статье. Вот что вы узнаете после прочтения данной статьи: что такое компаратор, как использовать стандартную сортировку, как сортировка работает для вектора или списка.
Массивы в C++
В данном уроке мы научимся работать с массивами. Массивы являются самым популярным способом хранения больших объемов данных. Поэтому давайте разбираться!
Set и multiset в C++
В данном уроке вы познакомитесь с двумя контейнерами C++, которые очень важны - set и multiset. По другому их еще называют множеством и мультимножеством. Они автоматически сортируются при добавлении нового элемента. Но у каждого из них есть своя особенность, которую мы разбираем в этой статье.
Стек (stack) в C++
В этой статье вы познакомитесь с структурой данных стек. Мы разберем что это за