Як визначити послідовний контейнер

Для того щоб визначити об'єкт контейнерного типу, необхідно спочатку увімкнути відповідний заголовний файл:

#include #inclnde #include #include

Визначення контейнера починається ім'ям його типу, за яким у кутових дужках слідує тип даних його елементів [12]. Наприклад:

vector svec;

Змінна svec визначається як вектор, здатний містити елементи типу string, а ilist як список з елементами типу int. Обидва контейнери при такому визначенні порожні. Щоб переконатися в цьому, можна викликати функцію-член empty():

if ( svec.empty() != true )

Найпростіший метод вставки елементів – використання функції-члена push_back(), яка додає елементи до кінця контейнера. Наприклад:

string text_word; while ( cin >> text_word )

Тут рядки зі стандартного введення зчитуються в змінну text_word, потім копія кожного рядка додається в контейнер svec за допомогою push_back().

Список має функцію-член push_front(), яка додає елемент у його початок. Нехай є такий масив:

for (int ix = 0; ix #include extern int get_word_count (string file_name); const int list_size = 64; list ilist (list_size);

Кожен елемент контейнера ініціалізується за замовчуванням, що відповідає типу даних. Для int це 0. Для рядкового типу викликається конструктор за замовчуванням класу string.

Ми можемо вказати початкове значення всіх елементів:

list ilist (list_size, -1);

vector svec(24, "pooh");

Дозволяється не лише задавати початковий розмір контейнера, алета згодом змінювати його за допомогою функції-члена resize(). Наприклад:

svec.resize( 2 * svec.size() );

Розмір svec у цьому прикладі подвоюється. Кожен новий елемент набуває значення за замовчуванням. Якщо ми хочемо ініціалізувати його якимось іншим значенням, воно вказується другим параметром функції-члена resize():

// кожен новий елемент набуває значення "piglet"

svec.resize(2 * svec.size(), "piglet");

До речі, якою є найбільш ймовірна ємність svec при визначенні, якщо його початковий розмір дорівнює 24? Правильно, 24! У випадку мінімальна ємність вектора дорівнює його поточному розміру. При подвоєнні розміру ємність, як правило, також подвоюється

Ми можемо ініціалізувати новий контейнер за допомогою наявного. Наприклад:

vector svec2(svec);

list ilist2(ilist);

Кожен контейнер підтримує повний набір операцій порівняння: рівність, нерівність, менше, більше, менше чи одно, більше чи одно. Порівнюються попарно всі елементи контейнера. Якщо вони рівні розміри контейнерів однакові, ці контейнери рівні; інакше – не рівні. Результат операцій "більше" або "менше" визначається порівнянням перших двох нерівних елементів. Ось що друкує програма, яка порівнює п'ять векторів:

ivecl: 1 3 5 7 9 12

ivec2: 0 1 1 2 3 5 8 13

// перший нерівний елемент: 1,

// ivecl більше ніж ivec2

ivecl ivec2 // true

ivec3 > ivecl //true

ivec5 > ivec2 // true

Існують три обмеження на тип елементів контейнера (практично це стосується лише класів користувача). Для цього повинні бути визначені:

  • операція “менша” (всі операції порівняння контейнерів, про якіговорилося вище, використовують лише ці дві операції порівняння);
  • значення за промовчанням (для класу це наявність конструктора за умовчанням).

Усі визначені типи даних, включаючи покажчики та класи зі стандартної бібліотеки С++, задовольняють цим вимогам.

Поясніть, що робить ця програма:

#include #include #include #int main() < vector svec; svec.reserve(1024); string text_word; while ( cin >> text_word ) svec.push_back( text_word ); svec.resize( svec.size()+svec.size()/2 ); //.

Чи може ємність контейнера бути меншою за його розмір? Чи бажано, щоб ємність дорівнювала розміру: спочатку або після вставки елемента? Чому?

Якщо програма з вправи 6.5 прочитає 256 слів, то яка найімовірніша ємність контейнера після зміни розміру? А якщо вона рахує 512 слів? 1000? 1048?

Які з цих класів не можуть зберігатися у векторі:

Ітератори

Ітератор надає узагальнений спосіб перебору елементів будь-якого контейнера – як послідовного, і асоціативного. Нехай iter є ітератором будь-якого контейнера. Тоді

ітератор переміщує так, що він вказує на наступний елемент контейнера, а

розіменовує ітератор, повертаючи елемент, який він вказує.

Всі контейнери мають функції-члени begin() та end().

  • begin() повертає ітератор, що вказує перший елемент контейнера.
  • end() повертає ітератор, що вказує на елемент, що йде за останнім у контейнері.

Щоб перебрати всі елементи контейнера, потрібно написати:

for ( iter = container. begin(); iter != container.end(); ++iter )

Оголошення ітераторавиглядає надто складним. Ось визначення пари ітераторів вектора типу string:

// vector vec; vector ::iterator iter = vec.begin();

vector :: iterator iter_end = vec.end();

У класі вектор для визначення ітератора використовується типовийефект. Синтаксис

Посилається на ітератор, визначений за допомогою typedef всередині класу вектор, що містить елементи типу string.

Для того, щоб надрукувати всі елементи вектора, потрібно написати:

for( ; iter != iter_end; ++iter )

cout void even_odd (const vector * pvec, vector * pvec_even, vector * pvec_odd ) < // const_iterator необхідний для навігації по pvec vector :: const_iterator c_iter = pvec->begin(); vector :: const_1terator c_iter_end = pvec->end(); for ( ; c_iter != c_iter_end; ++c_iter ) if ( *c_iter % 2 ) pvec_even->push_back( *c_iter ); else pvec_odd->push_back( *c_iter );

vector ::iterator iter = vec->begin()+vec.size()/2;

зсуває iter на два елементи.

Об'єкт контейнерного типу може бути ініціалізований парою ітераторів, що позначають початок і кінець послідовності елементів, що копіюються в новий об'єкт. (Другий ітератор повинен вказувати на елемент, наступний за останнім, що копіюється.) Припустимо, є вектор:

#include #include #include int main() < vector svec; string intext; while ( cin >> intext ) svec.push_back( intext ); // обробити svec.

Ось як можна визначити нові вектори, ініціалізуючи їх елементами першого вектора:

int main() < vector svec; //. // ініціалізація svec2 всіма елементами svec vector svec2(svec.begin(), svec.end()); // ініціалізація svec3 у першій половині svec vector ::iterator it = svec.begin() +svec.size()/2; vector svec3 (svec.begin(), it); //.

Використання спеціального типу istream_iterator (про нього розповідається в розділі 12.4.3) спрощує читання елементів із вхідного потоку до svec:

#include #include #include int mainQ < // прив'язка istream_iterator до стандартного введення istream_iterator infile(cin); // istream_iterator, що відзначає кінець потоку istream_iterator eos; // ініціалізація svec елементами, що зчитуються з cin; vector svec (infile, eos); //.

Крім ітераторів, для завдання діапазону значень, які ініціалізують контейнер, можна використовувати два покажчики масив вбудованого типу. Нехай є наступний масив рядків:

#include string words[4] = < "stately", "plump", "buck", "mulligan"

Ми можемо ініціалізувати вектор за допомогою покажчиків на перший елемент масиву і на наступний за останнім елемент:

vector vwords(words, words+4);

Другий покажчик служить “вартовим”: елемент, який він вказує, не копіюється.

Аналогічно можна ініціалізувати список цілих елементів:

int ia[6] = < 0, 1, 2, 3, 4, 5 >;

list ilist(ia, ia+6);

У розділі 12.4 ми знову звернемося до ітераторів та опишемо їх детальніше. Наразі інформації достатньо для того, щоб використовувати ітератори у нашій системі текстового пошуку. Але перш ніж повернутися до неї, розглянемо деякі додаткові операції, які підтримуються контейнерами.

Які помилки допущені під час використання ітераторів: