0 likes | 10 Views
The lecture covers the concept of containers in C++, specifically associative and sequential containers. Associative containers like sets and maps are associated with keys for element access, while sequential containers like arrays and vectors store elements in a specific order. Various types of sequential containers are explained, such as array, vector, list, forward_list, and deque, each offering different functionalities for managing data. The differences between these containers and their typical usage scenarios are discussed to help choose the appropriate container type based on requirements.
E N D
РАЗРАБОТКА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ МОБИЛЬНЫХ РОБОТОВ Лекция 5 Контейнеры
Контейнеры Часто при решении практических задач необходимо работать с массивами объектов. Ранее мы рассматривали использование массивов, требоваться дополнительный функционал. Для управления наборами объектов в стандартной библиотеке C++ определены контейнеры. Контейнер представляет определенного типа и позволяет и управлять доступом к этим элементам. В С++ есть два типа контейнеров: ассоциативные и последовательные. но на практике может коллекцию объектов 2
Ассоциативные контейнеры Ассоциативные контейнеры представляют такие контейнеры, где с каждым элементом ассоциирован некоторый ключ, и этот ключ применяется для доступа к элементу в контейнере. В С++ ассоциативные множествами (set) и картами/словарями (map) (а также unordered_set и unordered_map). (associative containers) контейнеры представлены 3
Последовательные контейнеры Последовательный контейнер хранит элементы последовательно, располагаются друг рядом с другом. Однако меняется их конкретного контейнера. (sequential container) элементы порядок в зависимости от 4
Типы последовательных контейнеров • array: коллекция фиксированного размера. Поддерживает произвольный доступ к любому элементу в контейнере. Добавлять или удалять элементы из контейнера нельзя. • vector: коллекция переменного размера. Поддерживает произвольный доступ к любому элементу в контейнере. Обеспечивает добавление и удаление элементов из любого места контейнера. • list: двухсвязный список Поддерживает только последовательный двухнаправленный доступ к элементам. Обеспечивает удаление и добавление элементов в начале и в конце контейнера. • forward_list: односвязный список. Поддерживает только однонаправленный последовательный доступ к элементам. Обеспечивает удаление и добавление элементов в начале и в конце контейнера. • deque: двусторонняя очередь. Поддерживает произвольный доступ к любому элементу в контейнере. Обеспечивает удаление и добавление элементов в начале и в конце контейнера. 5
Типы последовательных контейнеров Таким образом, стандартная библиотека C++ по умолчанию содержит ряд контейнеров, которые представляют данных. Все они имеют как некоторые возможности. За исключением класса array все они поддерживают добавление и удаление элементов. Основное различие между ними состоит в том, как они обеспечивают добавление и удаление элементов, а также доступ к элементам в контейнере. И, в зависимости от ситуации и потребностей, можно использовать тот или иной тип контейнеров. Например, если необходимо иметь возможность произвольный элемент контейнера, то применяется array или vector (с list или forwarded_list может потребоваться пробегаться по списку, чтобы найти нужный элемент). Если же необходимо иметь возможность добавлять или удалить элементы в середине контейнера, то можно применять list или forwarded_list, что с вектором сложнее сделать. Однако наиболее часто используется вектор, как более гибкий тип данных. Другие типы контейнеров применяются гораздо реже. определенные структуры общие, так и специфические 6
Адаптеры контейнеров последовательных называемые адаптеры контейнеров (container adaptor). Технически они не являются инкапсулируют один из вышеописанных контейнеров (например, вектор) и позволяют работать с этими контейнерами определенным образом. Это следующие типы • std::stack<>: представляет структуру данных "стек" • std::queue<>: представляет структуру данных "очередь" • std::priority_queue<>: также представляет очередь, но при этому ее элементы имеют приоритеты Кроме контейнеров есть так контейнерами, а 7
Vector #include <iostream> #include <vector> int main() { std::vector<int> numbers{ 1, 2, 3, 4, 5 }; int first = numbers.front(); // 1 int last = numbers.back(); // 5 int second = numbers[1]; // 2 numbers[0] = 6; // изменяем значение for (int n : numbers) std::cout << n << "\t"; // 6 2 3 4 5 std::cout << std::endl; } 8
Добавление элементов в vector #include <iostream> #include <vector> int main() { std::vector<int> numbers; // пустой вектор numbers.push_back(5); numbers.push_back(3); numbers.push_back(10); for (int n : numbers) cout << n << "\t"; // 5 3 10 std::cout << std::endl; } 9
Методы контейнера vector 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 erase(p): удаляет элемент, на который указывает итератор p. Возвращает итератор на элемент, следующий после удаленного, или на конец контейнера, если удален последний элемент erase(begin, end): удаляет элементы из диапазона, на начало и конец которого указывают итераторы begin и end. Возвращает итератор на элемент, следующий после последнего удаленного, или на конец контейнера, если удален последний элемент resize(n): оставляет в векторе n первых элементов. Если вектор содержит больше элементов, то его размер усекается до n элементов. Если размер вектора меньше n, то добавляются недостающие элементы и инициализируются значением по умолчанию resize(n, value): также оставляет в векторе n первых элементов. Если размер вектора меньше n, то добавляются недостающие элементы со значением value • • • • • • • • • 10
Итераторы #include <iostream> #include <vector> int main() { std::vector<int> numbers{ 10, 20, 30, 40 }; std::vector<int>::iterator итератор iter{ numbers.begin() }; // получаем while (iter != numbers.end()) // пока не дойдем до конца { std::cout << *iter << std::endl;// получаем элементы через итератор ++iter; // перемещаемся вперед на один элемент } // аналогичный пример с циклом for (std::vector<int>::iterator numbers.end(); start++) { for start{ numbers.begin() }; start != std::cout << *start << std::endl; } 11 }
Операции с итераторами *iter: получение элемента, на который указывает итератор ++iter: перемещение итератора вперед для обращения к следующему элементу --iter: перемещение итератора назад для обращения к предыдущему элементу. Итераторы контейнера forward_list не поддерживают операцию декремента. iter1 == iter2: два итератора равны, если они указывают на один и тот же элемент iter1 != iter2: два итератора не равны, если они указывают на разные элементы iter + n: возвращает итератор, который смещен от итератора iter на n позиций вперед iter - n: возвращает итератор, который смещен от итератора iter на n позиций назад iter += n: перемещает итератор на n позиций вперед iter -= n: перемещает итератор на n позиций назад iter1 - iter2: возвращает количество позиций между итераторами iter1 и iter2 >, >=, <, <=: операции сравнения. Один итератор больше другого, если указывает на элемент, который ближе к концу • • • • • • • • • • • 12
Array Контейнер array из одноименного модуля <array> представляет аналог массива. фиксированный размер. #include <array> Он также имеет #include <iostream> int main() { std::array<int, 5> numbers{ 2, 3, 4, 5, 6 }; // получаем значение элемента int n = numbers[2]; std::cout << "n = " << n << std::endl; // n = 4 // меняем значение элемента numbers[2] = 12; std::cout << "numbers[2] = " << numbers[2] << std::endl; // numbers[2] = 12 } 13
Методы array • • • • • size(): возвращает размер контейнера at(index): возвращает элемент по индексу index front(): возвращает первый элемент back(): возвращает последний элемент fill(n): присваивает всем значение n элементам контейнера 14
Array std::array<int, 5> numbers1{ 1, 2, 3, 4, 5 }; std::array<int, 5> numbers2 = numbers1; сделать // так можно int nums1[] = { 1,2,3,4,5 }; //int nums2[] = nums1; // так нельзя сделать 15
List Контейнер list представляет двухсвязный список, то есть такой список, где каждый элемент имеет указатели на предыдущий и последовательный элемент. Благодаря чему мы можем перемещаться по списку как вперед, так и назад. 16
List #include <iostream> #include <list> int main() { std::list<int> numbers{ 1, 2, 3, 4, 5 }; int first{ numbers.front() }; // 1 int last{ numbers.back() }; // 5 std::cout << "First: " << first << std::endl; std::cout << "Last: " << last << std::endl; // перебор в цикле for (int n : numbers) std::cout << n << "\t"; std::cout << std::endl; // перебор с помощью итераторов for (auto iter = numbers.begin(); iter != numbers.end(); iter++) { std::cout << *iter << "\t"; } std::cout << std::endl; } 17
Методы контейнера list resize(n): оставляет в списке n первых элементов. Если список содержит больше элементов, то он усекается до первых n элементов. Если размер списка меньше n, то добавляются недостающие инициализируются значением по умолчанию resize(n, value): также оставляет в списке n первых элементов. Если размер добавляются недостающие элементы со значением value assign(il): заменяет содержимое элементами из списка инициализации il assign(n, value): заменяет содержимое контейнера n элементами, которые имеют значение value assign(begin, end): заменяет содержимое контейнера элементами из диапазона, на начало и конец которого указывают итераторы begin и end • элементы и • списка меньше n, то • контейнера • • 18
Методы контейнера list push_back(val): добавляет значение val в конец списка push_front(val): добавляет значение val в начало списка emplace_back(val): добавляет значение val в конец списка emplace_front(val): добавляет значение val в начало списка emplace(pos, val): вставляет элемент val на позицию, на которую указывает итератор pos. Возвращает итератор на добавленный элемент insert(pos, val): вставляет элемент val на позицию, на которую указывает итератор pos, аналогично функции emplace. Возвращает итератор на добавленный элемент insert(pos, n, val): вставляет n элементов val начиная с позиции, на которую указывает итератор pos. Возвращает итератор на первый добавленный элемент. Если n = 0, то возвращается итератор pos. insert(pos, begin, end): вставляет начиная с позиции, на которую указывает итератор pos, элементы из другого контейнера из диапазона между итераторами begin и end. Возвращает итератор на первый добавленный элемент. Если между итераторами begin и end нет элементов, то возвращается итератор pos. insert(pos, values): вставляет список значений values начиная с позиции, на которую указывает итератор pos. добавленный элемент. Если values не содержит элементов, то возвращается итератор pos. • • • • • • • • • Возвращает итератор на первый 19
Методы контейнера list clear(p): удаляет все элементы pop_back(): удаляет последний элемент pop_front(): удаляет первый элемент erase(p): удаляет элемент, на который указывает итератор p. Возвращает следующий после удаленного, контейнера, если удален последний элемент erase(begin, end): удаляет элементы из диапазона, на начало и конец которого указывают итераторы begin и end. Возвращает итератор на элемент, следующий после последнего удаленного, контейнера, если удален последний элемент • • • • итератор на элемент, конец или на • или на конец 20
Контейнер forward_list Контейнер forward_list представляет односвязный список, то есть такой список, где каждый элемент хранит указатель на следующий элемент: #include <iostream> #include <forward_list> int main() { std::forward_list<int> numbers{ 1, 2, 3, 4, 5 }; auto current = numbers.begin(); // итератор на начало списка auto end = numbers.end(); // указатель на конец списка while (current != end) { std::cout << *current << "\t"; current++; } std::cout << std::endl; } 21
Методы forward_list empty() позволяет узнать, пуст ли список. Если он пуст, то функция возвращает значение true, иначе возвращается значение false resize(n): оставляет в списке n первых элементов. Если список содержит больше элементов, то он усекается до первых n элементов. Если размер списка меньше n, то добавляются недостающие элементы и инициализируются значением по умолчанию resize(n, value): также оставляет в списке n первых элементов. Если размер списка меньше n, то добавляются недостающие элементы со значением value assign(il): заменяет содержимое контейнера элементами из списка инициализации il assign(n, value): заменяет элементами, которые имеют значение value assign(begin, end): заменяет элементами из диапазона, на начало и конец которого указывают итераторы begin и end • • • • • содержимое контейнера n • содержимое контейнера 22
Методы forward_list push_front(val): добавляет объект val в начало списка emplace_front(val): добавляет объект val в начало списка emplace_after(p, val): вставляет объект val после элемента, на который указывает итератор вставленный элемент. Если p представляет итератор на позицию после конца списка, то результат неопределен. insert_after(p, val): вставляет объект val после элемента, на который указывает итератор вставленный элемент. insert_after(p, n, val): вставляет n объектов val после элемента, на который указывает итератор последний вставленный элемент. insert_after(p, begin, end): вставляет после элемента, на который указывает итератор p, набор объектов из другого контейнера, начало и конец которого определяется итераторами begin и end. Возвращает итератор на последний вставленный элемент. insert_after(p, il): вставляет после указывает итератор p, список инициализации il. Возвращает итератор на последний вставленный элемент. • • • p. Возвращает итератор на • p. Возвращает итератор на • p. Возвращает итератор на • • элемента, на который 23
Методы forward_list clear(): удаляет все элементы pop_front(): удаляет первый элемент erase_after(p): удаляет элемент после элемента, на который указывает итератор p. Возвращает итератор на элемент после удаленного erase_after(begin, end): удаляет диапазон элементов, на начало и конец которого указывают соответственно итераторы begin и end. Возвращает итератор на элемент после последнего удаленного • • • • 24
Контейнер deque Deque представляет двухстороннюю очередь. #include <iostream> #include <deque> int main() { std::deque<int> numbers{ 1, 2, 3, 4, 5 }; for (int n : numbers) std::cout << n << "\t"; std::cout << std::endl; for (unsigned i{}; i < numbers.size(); i++) std::cout << numbers[i] << "\t"; std::cout << std::endl; for (auto iter = numbers.begin(); iter != numbers.end(); iter++) std::cout << *iter << "\t"; std::cout << std::endl; } 25
Методы deque • • size() определяет размер очереди empty() позволяет элементы resize(n): оставляет в очереди n первых элементов. Если deque содержит больше контейнера усекается до первых n элементов. Если размер очереди меньше недостающие элементы значением по умолчанию resize(n, value): также оставляет в очереди n первых элементов. Если размер добавляются недостающие элементы со значением value узнать, содержит ли очередь • элементов, то размер n, то инициализируются добавляются и • очереди меньше n, то 26
Методы deque заменяет • assign(il): элементами из списка инициализации il assign(n, value): заменяет содержимое контейнера n элементами, которые имеют значение value assign(begin, end): заменяет содержимое контейнера элементами из диапазона, на начало и конец которого указывают итераторы begin и end содержимое контейнера • • 27
Методы deque • • • • • push_back(val): добавляет значение val в конец очереди push_front(val): добавляет значение val в начало очереди emplace_back(val): добавляет значение val в конец очереди emplace_front(val): добавляет значение val в начало очереди emplace(pos, val): вставляет элемент val на позицию, на которую указывает итератор pos. Возвращает итератор на добавленный элемент insert(pos, val): вставляет элемент val на позицию, на которую указывает итератор pos, аналогично функции emplace. Возвращает итератор на добавленный элемент insert(pos, n, val): вставляет n элементов val начиная с позиции, на которую указывает итератор pos. Возвращает итератор на первый добавленный элемент. Если n = 0, то возвращается итератор pos. insert(pos, begin, end): вставляет начиная с позиции, на которую указывает итератор pos, элементы из другого контейнера из диапазона между итераторами begin и end. Возвращает итератор на первый добавленный элемент. Если между итераторами begin и end нет элементов, то возвращается итератор pos. insert(pos, values): вставляет список значений values начиная с позиции, на которую указывает итератор pos. добавленный элемент. Если values не содержит элементов, то возвращается итератор pos. • • • • Возвращает итератор на первый 28
Методы deque • • • • clear(p): удаляет все элементы pop_back(): удаляет последний элемент pop_front(): удаляет первый элемент erase(p): удаляет элемент, на который указывает итератор p. Возвращает следующий после удаленного, контейнера, если удален последний элемент erase(begin, end): удаляет элементы из диапазона, на начало и конец которого указывают итераторы begin и end. Возвращает итератор на элемент, следующий после последнего удаленного, контейнера, если удален последний элемент итератор на элемент, конец или на • или на конец 29
Стек std::stack std::stack<T> представляет данных, которая работает по принципу LIFO (last-in first- out или "последний вошел — первым вышел") — первым всегда извлекается последний добавленный элемент. Стек можно сравнить со стопкой стопкой тарелок - тарелки добавляются сверху, каждая последующая тарелка кладется поверх предыдущей. А если надо взять тарелку, то сначала берется та, которая в самом верху (которую положили самой последней). Класс стек - структуру предметов, например, 30
Стек std::stack #include <iostream> #include <stack> int main() { std::stack<std::string> stack; // добавляем три элемента stack.push("Tom"); stack.push("Bob"); stack.push("Sam"); std::cout << "stack size: " << stack.size() << std::endl; // stack size: 3 } 31
Очередь std::queue Класс std::queue<T> представляет очередь - контейнер, который работает по принципу FIFO (first-in first-out или "первый вошел — первым вышел") — первым всегда извлекается первый добавленный элемент. То есть это контейнер, аналогичный стандартной очереди, которая часто встречается в нашей повседневной жизни. 32
Очередь std::queue #include <iostream> #include <queue> int main() { std::queue<std::string> queue; // пустая очередь // добавляем три элемента queue.push("Tom"); queue.push("Bob"); queue.push("Sam"); std::cout << "queue size: " << queue.size() << std::endl; // queue size: 3 } 33
Множества (set) Множество (set) представляет такой контейнер, который может хранить только уникальные значения. Как правило, множества применяются для создания коллекций, которые не должны иметь дубликатов. #include <iostream> #include <set> int main() { std::set<int> numbers{ 3, 4, 5 }; numbers.insert(1); numbers.insert(2); numbers.insert(2); numbers.insert(2); numbers.insert(6); for (int n : numbers) std::cout << n << "\t"; // 1 2 3 4 5 6 std::cout << std::endl; } 34
Неупорядоченное множество unordered_set #include <iostream> #include <set> int main() { std::set<int> numbers{ 3, 2, 5, 4 }; numbers.insert(1); numbers.insert(6); for (int n : numbers) std::cout << n << "\t"; // 6 1 4 5 2 3 std::cout << std::endl; } 35
Словарь std::map Карта или std::map представляет контейнер, где каждое значение ассоциировано с определенным ключом. И по этому ключу можно получить элемент. Причем ключи могут иметь значения. Примером такого контейнера может служить словарь, где каждому слову сопоставляется его перевод или объяснение. Поэтому такие структуры еще называют словарями. только уникальные 36
Словарь std::map #include <iostream> #include <map> int main() { std::map<std::string, std::string> capital; // установка значений capital["Russia"] = "Moscow"; capital["China"] = "Beijing"; capital["USA"] = "Washington"; // получение значений std::cout << "Russia\t" << capital["Russia"] << std::endl; std::cout << "China\t" << capital["China"] << std::endl; std::cout << "USA\t" << capital["USA"] << std::endl; } 37
Итоги Контейнер vector ведет себя как массив, но может автоматически увеличиваться по мере необходимости. Он поддерживает прямой доступ и связанное хранение и имеет очень гибкую длину. По этим и многим другим причинам контейнер vector является наиболее предпочтительным последовательным большинства областей применения. Если вы сомневаетесь в выборе вида последовательного контейнера, начните с использования вектора. Контейнер array имеет некоторые сильные стороны vector, но длина не так гибка. Контейнер deque (двусторонняя очередь) обеспечивает быструю вставку и удаление в начале и в конце контейнера. Он делится преимуществами vector случайного доступа и гибкой длины, но не является непрерывным. list Контейнер — это двунаправленный обеспечивает двунаправленный доступ, быстрые вставки и быстрые удаления в любом месте контейнера, но вы не можете случайно получить доступ к элементу в контейнере. Контейнер forward_list — однонаправленный список. Это версия контейнера list только с доступом в прямом направлении. контейнером для список, который 38
Итоги В ассоциативных контейнерах элементы вставляются в предварительно определенном порядке — например, с сортировкой по возрастанию. Также доступны неупорядоченные Ассоциативные контейнеры можно объединить в два подмножества: сопоставления (set) и наборы (map). Контейнер map, который иногда называют словарем, состоит из пар "ключ-значение". Ключ используется последовательности, а значение связано с ключом. Например, map может содержать ключи, представляющие каждое уникальное ключевое слово в тексте, и соответствующие значения, которые обозначают количество повторений каждого слова в тексте. map — это неупорядоченная версия unordered_map. set — это контейнер уникальных элементов, упорядоченных по возрастанию. Каждое его значение также является и ключом. set — это неупорядоченная версия unordered_set. Контейнеры map и set разрешают вставку только одного экземпляра ключа или элемента. Если необходимо включить несколько экземпляров элемента, следует использовать контейнер multimap или multiset. Неупорядоченные версии этих контейнеров — unordered_multimap и unordered_multiset. Упорядоченные контейнеры двунаправленные итераторы, а их итераторы с перебором в прямом направлении. ассоциативные контейнеры. для упорядочивания map и set поддерживают аналоги неупорядоченный — 39
РАЗРАБОТКА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ МОБИЛЬНЫХ РОБОТОВ Лекция 5 Контейнеры