Container class in cpp

Containers

A container is a holder object that stores a collection of other objects (its elements). They are implemented as class templates, which allows a great flexibility in the types supported as elements.

The container manages the storage space for its elements and provides member functions to access them, either directly or through iterators (reference objects with similar properties to pointers).

Containers replicate structures very commonly used in programming: dynamic arrays (vector), queues (queue), stacks (stack), heaps (priority_queue), linked lists (list), trees (set), associative arrays (map).

Many containers have several member functions in common, and share functionalities. The decision of which type of container to use for a specific need does not generally depend only on the functionality offered by the container, but also on the efficiency of some of its members (complexity). This is especially true for sequence containers, which offer different trade-offs in complexity between inserting/removing elements and accessing them.

stack, queue and priority_queue are implemented as container adaptors. Container adaptors are not full container classes, but classes that provide a specific interface relying on an object of one of the container classes (such as deque or list) to handle the elements. The underlying container is encapsulated in such a way that its elements are accessed by the members of the container adaptor independently of the underlying container class used.

Container class templates

Sequence containers:
array Array class (class template) vector Vector (class template) deque Double ended queue (class template) forward_list Forward list (class template) list List (class template)
Container adaptors:
stack LIFO stack (class template) queue FIFO queue (class template) priority_queue Priority queue (class template)
Associative containers:
set Set (class template) multiset Multiple-key set (class template) map Map (class template) multimap Multiple-key map (class template)
Unordered associative containers:
unordered_set Unordered Set (class template) unordered_multiset Unordered Multiset (class template) unordered_map Unordered Map (class template) unordered_multimap Unordered Multimap (class template)
Other:
Two class templates share certain properties with containers, and are sometimes classified with them: bitset and valarray .

Читайте также:  Javascript check if any radio checked

Member map

This is a comparison chart with the different member functions present on each of the different containers:

Legend:

C++98 Available since C++98
C++11 New in C++11

Sequence containers

Headers
Members array vector deque forward_list list
constructor implicit vector deque forward_list list
destructor implicit ~vector ~deque ~forward_list ~list
operator= implicit iterators begin begin begin begin begin
before_begin
begin
end end end end end end
rbegin rbegin rbegin rbegin rbegin
rend rend rend rend rend
const iterators cbegin cbegin cbegin cbegin cbegin
cbefore_begin
cbegin
cend cend cend cend cend cend
crbegin crbegin crbegin crbegin crbegin
crend crend crend crend crend
capacity size size size size size
max_size max_size max_size max_size max_size max_size
empty empty empty empty empty empty
resize resize resize resize resize
shrink_to_fit shrink_to_fit shrink_to_fit
capacity capacity
reserve reserve
element access front front front front front front
back back back back back
operator[] operator[] operator[] operator[]
at at at at
modifiers assign assign assign assign assign
emplace emplace emplace emplace_after emplace
insert insert insert insert_after insert
erase erase erase erase_after erase
emplace_back emplace_back emplace_back emplace_back
push_back push_back push_back push_back
pop_back pop_back pop_back pop_back
emplace_front emplace_front emplace_front emplace_front
push_front push_front push_front push_front
pop_front pop_front pop_front pop_front
clear clear clear clear clear
swap swap swap swap swap swap
list operations splice splice_after splice
remove remove remove
remove_if remove_if remove_if
unique unique unique
merge merge merge
sort sort sort
reverse reverse reverse
observers get_allocator get_allocator get_allocator get_allocator get_allocator
data data data

Associative containers

Headers
Members set multiset map multimap unordered_set unordered_multiset unordered_map unordered_multimap
constructor set multiset map multimap unordered_set unordered_multiset unordered_map unordered_multimap
destructor ~set ~multiset ~map ~multimap ~unordered_set ~unordered_multiset ~unordered_map ~unordered_multimap
assignment iterators begin begin begin begin begin begin begin begin begin
end end end end end end end end end
rbegin rbegin rbegin rbegin rbegin
rend rend rend rend rend
const iterators cbegin cbegin cbegin cbegin cbegin cbegin cbegin cbegin cbegin
cend cend cend cend cend cend cend cend cend
crbegin crbegin crbegin crbegin crbegin
crend crend crend crend crend
capacity size size size size size size size size size
max_size max_size max_size max_size max_size max_size max_size max_size max_size
empty empty empty empty empty empty empty empty empty
reserve reserve reserve reserve reserve
element access at at at
operator[] operator[] operator[]
modifiers emplace emplace emplace emplace emplace emplace emplace emplace emplace
emplace_hint emplace_hint emplace_hint emplace_hint emplace_hint emplace_hint emplace_hint emplace_hint emplace_hint
insert insert insert insert insert insert insert insert insert
erase erase erase erase erase erase erase erase erase
clear clear clear clear clear clear clear clear clear
swap swap swap swap swap swap swap swap swap
operations count count count count count count count count count
find find find find find find find find find
equal_range equal_range equal_range equal_range equal_range equal_range equal_range equal_range equal_range
lower_bound lower_bound lower_bound lower_bound lower_bound
upper_bound upper_bound upper_bound upper_bound upper_bound
observers get_allocator get_allocator get_allocator get_allocator get_allocator get_allocator get_allocator get_allocator get_allocator
key_comp key_comp key_comp key_comp key_comp
value_comp value_comp value_comp value_comp value_comp
key_eq key_eq key_eq key_eq key_eq
hash_function hash_function hash_function hash_function hash_function
buckets bucket bucket bucket bucket bucket
bucket_count bucket_count bucket_count bucket_count bucket_count
bucket_size bucket_size bucket_size bucket_size bucket_size
max_bucket_count max_bucket_count max_bucket_count max_bucket_count max_bucket_count
hash policy rehash rehash rehash rehash rehash
load_factor load_factor load_factor load_factor load_factor
max_load_factor max_load_factor max_load_factor max_load_factor max_load_factor

Источник

STL для новичков. Реализация класса-контейнера

Привет Хабр, наверное все, кто изучает С++ хотят разобраться как реализованы и как работают классы-контейнеры из стандартной библиотеки. Как по мне, чтобы лучше освоить нечто похожее на контейнеры, то надо попробовать реализовать один из контейнеров самому. В этой статье я хочу показать вам хотя бы примерно как реализуются классы-контейнеры на примере списка. Хочу сразу сказать, что это не будет копирование всего функционала, а будет показана только концепция работы контейнера, а именно реализуем класс списка и класс итератора для работы с ним.

Статья будет интересна только новичкам, которые начинают изучать стандартную библиотеку, профессионалы не найдут здесь для себя ничего новго.

Ну что ж, начнем. Что собой представляет list из стандартной библиотеки? Это последовательный контейнер, который оптимизирован для вставки и удаления элементов. Для работы с этим контейнером в STL используется двунаправленный итератор, который мы попробуем реализовать. Также мы реализуем функцию вставки на начало и в конец списка, вставку после элемента на который указывает iterator, удаление элементов и еще несколько функций.

А сейчас будет много кода с комментариями.
файл «dlist.h»

#ifndef DLIST_H_ #define DLIST_H_ #include template class Double_list < public: class iterator; friend class iterator; //класс итератора должен иметь доступ к полям класса Double_list private: class Double_node; friend class Double_node; class Double_node // < public: friend class Double_list; friend class iterator; //Создать вузол с какимто значением типа T Double_node(T node_val): val(node_val) <> Double_node() <> ~Double_node() <> void print_val() Double_node *next; //указывает на следующий узел списка Double_node *prev; //указывает на предыдущий узел. T val; //данные. >; public: class iterator < friend class Double_list; public: // нулевой конструкрор iterator() :the_node(0) <> //здесь мы создаем итератор с указателя на узел Double_node iterator(Double_node *dn): the_node(dn) <> //конструктор копии iterator(const iterator &it): the_node(it.the_node) <> iterator& operator=(const iterator &it) < the_node = it.the_node; return *this; >// в этом классе оператор == означает, что два итератора указывают на // один и тот же узел bool operator == (const iterator &it) const < return (the_node == it.the_node); >bool operator!=(const iterator &it) const < return !(it == *this); >//переводит итератор на следующий узел списка. iterator& operator++() < if (the_node == 0) throw "incremented an empty iterator"; if (the_node->next == 0) throw "tried to increment too far past the end"; the_node = the_node->next; return *this; > //переводит итератор на предідущий узел списка. iterator & operator--() < if (the_node == 0) throw "decremented an empty iterator"; if (the_node->prev == 0) throw "tried to decrement past the beginning"; the_node = the_node->prev; return *this; > //Возвращает значение данных. T& operator*() const < if (the_node == 0) throw "tried to dereference an empty iterator"; return the_node->val; > private: Double_node *the_node; >; private: Double_node *head; //указывает на начало списка. Double_node *tail; //указывает на елемент, который идет за последним iterator head_iterator; //итератор, который всегда указывает на начало списка iterator tail_iterator; //итератор, который всегда указывает на элемент, который находится за последним. public: Double_list() < head = tail = new Double_node; tail->next = nullptr; tail->prev = nullptr; //инициализирует итераторы head_iterator = iterator(head); tail_iterator = iterator(tail); > //Создать список, который содержит один элемент. Double_list(T node_val) < head = tail = new Double_node; tail->next = nullptr; tail->prev = 0; head_iterator = iterator(head); tail_iterator = iterator(tail); add_front(node_val); > ~Double_list() < Double_node *node_to_delete = head; for (Double_node *sn = head; sn != tail;) < sn = sn->next; delete node_to_delete; node_to_delete = sn; > delete node_to_delete; > bool is_empty() iterator front() iterator rear() //вставляем узел в начало списка void add_front(T node_val) < Double_node *node_to_add = new Double_node (node_val); node_to_add->next = head; node_to_add->prev = nullptr; head->prev = node_to_add; head = node_to_add; //так как head изменился, нужно изменить head_iterator head_iterator= iterator(head); > //вставляем узел в конец списка void add_rear(T node_val) < if (is_empty()) add_front(node_val); else < Double_node *node_to_add = new Double_node(node_val); node_to_add->next = tail; node_to_add->prev = tail->prev; tail->prev->next = node_to_add; tail->prev = node_to_add; //изменяем tail_iterator tail_iterator = iterator(tail); > > //вставляет в спсиок node_val после итератора key_i bool insert_after(T node_val, const iterator &key_i) < for (Double_node *dn = head; dn != tail; dn = dn->next) < //Найден ли узел для заданного итератора if (dn == key_i.the_node) < Double_node *node_to_add = new Double_node(node_val); node_to_add->prev = dn; node_to_add->next = dn->next; dn->next->prev = node_to_add; dn->next = node_to_add; return true; > > return false; > //Удалить первый элемент списка. T remove_front() < if (is_empty()) throw "tried to remove from an empty list"; Double_node *node_to_remove = head; T return_val = node_to_remove->val; head = node_to_remove->next; head->prev = 0; head_iterator = iterator(head); delete node_to_remove; return return_val; > T remove_rear() < if (is_empty()) throw "tried to remove from an empty list"; Double_node *node_to_remove = tail->prev; if(node_to_remove->prev == 0) < return remove_front(); >else < T return_val = node_to_remove->val; node_to_remove->prev->next = tail; tail->prev = node_to_remove->prev; delete node_to_remove; return return_val; > > bool remove_it(iterator &key_i) < for (Double_node *dn = head; dn != tail; dn = dn-next) < //Найден ли узел для заданного итератора if (dn == key+i.the_node) < dn->prev->next = dn->next; dn->next->prev = dn->prev; delete dn; key_i.the_node =0; return true; > > return false; > //Возвращает первый итератор, указывающий на node_val iterator find(T node_val) const < for (double_node *dn = head; dn != tail; dn = dn->next) < if (dn->val == node_val) return iterator(dn); > //Если node_val нет в списке возвращает tail_iterator return tail_iterator; > //возвращает итератор, который указывает на n-ый элемент списка iterator get_nth(const int element_num) const < if (element_num < 1) return tail_iterator; int count = 1; for(Double_node *dn = head; dn != tail; dn = dn->next) < if (count++ == element_num) return iterator(dn); >return tail_iterator; > //Количество узлов в списке. int size() const < int count = 0; for (Double_node *dn = head; dn != tail; dn = dn->next) ++count; return count; > void print() const < for (Double_node *dn = head; dn!= tail; dn = dn->next) < dn->print_val(); > std::cout >; #endif 
#include "dlist.h" int main() < Double_listthe_list; Double_list::iterator list_iter; for (int i=0; i the_list.print(); the_list.remove_front(); for (list_iter = the_list.front(); list_iter != the_list.rear(); ++ list_iter) < std::cout std::cout << std:: endl; //выводим в обратном порядке for (list_iter = the_list.rear(); list_iter != the_list.front();) < --list_iter; std::cout std::cout
Анализ кода

Итератор реализован как открытый вложенный класс. Так как класс открытый, пользователи могут создавать объекты. Класс iterator должен знать о некоторых закрытых элементах класса Double_list, поэтому мы объявляем класс iterator дружественным к классу Double_list и так же в классе iterator объявляем другом класс Double_list.

Теперь посмотрим на внутреннее устройство класса Double_list::iterator. У него есть единственный элемент данных: Double_node* the_node. Именно это итератор и должен скрывать. Операции, объявляемые в классе iterator, позволяют пользователям манипулировать этим узлом определенным образом.

Конец

Вот на этом и все. Примерно так реализован класс списка в библиотеке STL. Конечно, наш класс очень общий пример, в STL все сложнее, но общий принцип с этого кода понять можно.

Источник

Оцените статью