Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Namespace CounterNameSpace 3 страница. cout << A_ob << A_ob;




 

cout << A_ob << A_ob;

cout << A_ob+100; // Відображає число 124, оскільки тут виконується

// перетворення об'єкта класу у значення типу int.

cout << "\n";

A_ob = A_ob + A_ob; // Додавання двох об'єктів класу kooClass

// Виконується без перетворення типу.

cout << A_ob; // Відображає координати 3, 5, 7

 

getch (); return 0;

}

Внаслідок виконання цієї програми на моніторі буде відображено такі результати:

1,2, 3

2, 3, 4

3, 5, 7

Як підтверджують результати виконання цієї програми, якщо в такому виразі цілочисельного типу, як cout << A_ob+100, використовується об'єкт типу kooClass, то до цього об'єкта застосовується функція перетворення. У цьому випадку функція перетворення повертає значення 24, яке потім бере участь в операції додавання з числом 100. Але коли у перетворенні немає потреби, наприклад, під час обчислення виразу A_ob = A_ob + A_ob, то функція перетворення не викликається.

Якщо функція перетворення створена, то вона викликатиметься скрізь, де потрібне перетворення, враховуючи ситуації, коли об'єкт передається функції як аргумент. Наприклад, якщо об'єкт класу kooClass передати стандартній функції abs (), то також буде викликано функцію, що виконує перетворення об'єкта типу kooClass у значення типу int, оскільки функція abs () повинна приймати аргумент цілочисельного типу.

Необхідно пам'ятати! Для різних ситуацій можна створювати різні функції перетворення. Наприклад, можна визначити функції, які перетворять об'єкти типу kooClass у значення типу double або long, при цьому створені функції будуть застосовуватися автоматично. Функції перетворення дають змогу інтегрувати нові типи класів, що створюються програмістом, у С++-середовище програмування.


Розділ 22. Введення в стандартну бібліотеку шаблонів

Матеріал цього розділу містить інформацію про те, яку багато хто з програмістів вважає найважливішим доповненням у мові програмування C++ за останні роки. Йдеться про стандартну бібліотеку шаблонів (Standard Template Library – STL). Саме внесення бібліотеки STL у мову програмування C++ було основною подією, яка наполегливо обговорювалася у період стандартизації мови C++. Бібліотека STL надає користувачу шаблонні класи і функції загального призначення, які реалізують багато популярних і часто використовуваних алгоритмів і структур даних. Наприклад, вона містить підтримку векторів, списків, черг і стеків, а також визначає різні функції, які забезпечують до них доступ. Оскільки бібліотека STL складається з шаблонних класів, то алгоритми і структури даних можна застосовувати до даних практично будь-якого типу.

Бібліотека STL – це набір шаблонних класів і функцій загального призначення.

Бібліотека STL – це результат розроблення стандартного програмного забезпечення, який увібрав у себе одні з найскладніших засобів мови програмування C++. Щоб розуміти вміст бібліотеки STL і уміти ним користуватися, необхідно досконало засвоїти весь матеріал, викладений у попередніх розділах. Особливо добре потрібно орієнтуватися в шаблонах. Відверто кажучи, шаблонний синтаксис, який використовує бібліотека STL, може спочатку налякати програмістів-початківців, хоча він виглядає дещо складнішим, ніж є насправді. Незважаючи на те, що матеріал цього розділу є не надто важчим за будь-який інший, наведений у цьому навчальному посібнику, проте не варто засмучуватися від того, якщо щось на перший погляд Вам видасться незрозумілим. Трохи терпіння під час перегляду прикладів і певної уваги у процесі їх детального аналізу приведе до того, що незабаром Ви зрозумієте, як за незвичним синтаксисом ховається строга простота бібліотеки STL.

STL – достатньо велика бібліотека, і всі її засоби неможливо викласти в одному розділі. Повний опис бібліотеки STL зі всіма її нюансами і методами програмування займає цілу книгу [2, 24, 31]. Мета представленого тут матеріалу – познайомити студентів з основними операціями, принципами проектування і основами STL-програмування. Засвоївши матеріал цього розділу, студент зможете самостійно легко вивчити іншу частину бібліотеки STL.

У цьому розділі також описано ще один важливий клас C++ – клас string. Він призначений для визначення рядкового типу даних, який дає змогу обробляти символьні рядки подібно до того, як обробляються дані інших типів: за допомогою операторів. Клас string тісно пов'язаний з бібліотекою STL.

22.1. Огляд стандартної бібліотеки шаблонів

Незважаючи на великий розмір стандартної бібліотеки шаблонів (STL) та інколи лякливий синтаксис, насправді її засоби достатньо легко використовувати, якщо зрозуміти, як вона побудована і з яких елементів складається. Тому, перш ніж переходити до перегляду прикладів, познайомимося з основними складовими бібліотеки STL.

Ядро стандартної бібліотеки шаблонів містить три основні елементи: контейнери, алгоритми і ітератори. Вони працюють спільно один з іншим, надаючи тим самим користувачу готові розв'язки різних задач програмування.

Контейнери – це об'єкти, які містять інші об'єкти.

Існує декілька різних типів контейнерів. Наприклад, клас vector визначає динамічний масив, клас queue створює двосторонню чергу, а клас list забезпечує роботу з лінійним переліком. Ці контейнери називають послідовними контейнерами і є базовими в бібліотеці STL. Окрім базових, бібліотека STL визначає асоціативні контейнери, які дають змогу ефективно знаходити потрібні значення на основі заданих ключових значень (ключів). Наприклад, клас map забезпечує зберігання пар "ключ-значення" і надає можливість знаходити значення за заданим унікальним ключем.

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

Алгоритми обробляють вміст контейнерів.

Їх можливості містять засоби ініціалізації, сортування, пошуку і перетворення вмісту контейнерів. Багато алгоритмів працюють із заданим діапазоном елементів контейнера.

Ітератори подібні до покажчиків.

Ітератори – це об'єкти, які тою чи іншою мірою діють подібно до покажчиків. Вони дають змогу циклічно здійснювати запит до вмісту контейнера практично так само, як це робиться за допомогою покажчика під час циклічного запиту елементів масиву. Існує п'ять типів ітераторів.

Табл. 22.1. Типи ітераторів

Ітератори Опис
Довільного доступу (random access) Зберігають і зчитують значення; дають змогу організувати довільний доступ до елементів контейнера
Двоспрямовані (bidirectional) Зберігають і зчитують значення; забезпечують інкрементно-декрементне переміщення
Односпрямовані (forward) Зберігають і зчитують значення; забезпечують тільки інкрементне переміщення
Вхідні (input) Зчитують, але не записують значення; забезпечують тільки інкрементне переміщення
Вихідні (output) Записують, але не зчитують значення; забезпечують тільки інкрементне переміщення

У загальному випадку ітератор, який має великі можливості доступу, можна використовувати замість ітератора з меншими можливостями. Наприклад, односпрямованим ітератором можна замінити вхідний ітератор.

Ітератори обробляються аналогічно до покажчиків. Їх можна інкрементувати і декрементувати|. До них можна застосовувати оператор перейменування адреси *. Ітератори оголошуються за допомогою типу iterator, що визначається різними контейнерами.

Бібліотека STL підтримує реверсивні ітератори, які є або двоспрямованими, або ітераторами довільного доступу, даючи змогу переміщатися по послідовності у зворотному порядку. Отже, якщо реверсивний ітератор вказує на кінець послідовності, то після інкрементування він вказуватиме на елемент, розташований перед кінцем послідовності.

При посиланні на різні типи ітераторів у описах шаблонів у цьому навчальному посібнику буде використано такі терміни:

Термін Наданий ітератор
BiIter Двоспрямований
ForIter Односпрямований
InIter Вхідний
OutIter Вихідний
RandIter Довільного доступу

Бібліотека STL опирається не тільки на контейнери, алгоритми і ітератори, але і на інші стандартні компоненти. Основними з них є розподільники пам'яті, предикати і функції порівняння.

Розподільник пам'яті керує виділенням пам'яті для контейнера.

Кожен контейнер має свій розподільник пам'яті (allocator). Розподільники керують виділенням пам'яті для контейнера. Стандартний розподільник – це об'єкт класу allocator, але у разі потреби (у спеціалізованих додатках) можна визначати власні розподільники. Здебільшого стандартного розподільника є цілком достатньо для реалізації програмних задумів.

Предикат повертає як результат значення ІСТИНА/ФАЛЬШ.

Деякі алгоритми і контейнери використовують спеціальний тип функції, так званий предикат (predicate). Існує два варіанти предикатів: унарний і бінарний. Унарний предикат приймає один аргумент, а бінарний – два. Ці функції повертають значення ІСТИНА/ФАЛЬШ, але точні умови, які примусять їх повернути дійсне або помилкове значення, визначаються програмістом. В іншій частині цього розділу, коли знадобиться унарна функція-предикат, на це буде вказано за допомогою типу UnPred. У разі потреби використання бінарного предиката застосовуватиметься тип BinPred. У бінарному предикаті аргументи завжди розташовані у порядку перший, другий стосовно функції, яка викликає цей предикат. Як для унарного, так і для бінарного предикатів аргументи повинні містити значення, тип яких збігається з типом об'єктів, які зберігає цей контейнер.

Функції порівняння порівнюють два елементи послідовності.

Деякі алгоритми і класи використовують спеціальний тип бінарного предиката, який порівнює два елементи. Функції порівняння повертають значення true, якщо їх перший аргумент менший від другого. Функції порівняння ідентифікуються за допомогою типу Comp.

Крім заголовків, потрібних різними класами STL, стандартна бібліотека C++ містить заголовки < utility > і < functional >, які забезпечують підтримку STL. Наприклад, у заголовку <utility> визначається шаблонний клас pair, який може зберігати пару значень. Будемо використовувати клас pair нижче у цьому розділі.

Шаблони у заголовку < functional > дають змогу створювати об'єкти, які визначають функцію operator (). Ці об'єкти називаються об'єктами-функціями, і їх у багатьох випадках можна використовувати замість покажчиків на функції. Існує декілька вбудованих об'єктів-функцій, оголошених у заголовку < functional >. Наведемо тут деякі з них:

plus minus multiplies divides modulus
negate equal_to not_equal_to greater greater_equal
less less_equal logical_and logical_or logical_not

Можливо, найширше використовується об'єкт-функція less, який визначає, за яких умов один об'єкт менший від іншого. Об'єкти-функції можна використовувати замість реальних покажчиків на функції в алгоритмах бібліотеки STL, про які піде мова нижче. Використовуючи об'єкти-функції замість покажчиків на функції, бібліотека STL у деяких випадках генерує ефективніший програмний код.

Матеріал цього розділу не передбачає використання об'єктів-функцій, і ми не застосовуватимемо їх безпосередньо. Детальний опис об'єктів-функцій можна знайти в книзі Герберта Шілдта " Полный справочник по C++" [27].

22.2. Поняття про контейнерні класи

Як ми вже зазначали вище, контейнери є об'єктами бібліотеки STL, основне призначення яких –зберігання даних. Контейнери, що визначаються в бібліотеці STL, представлено в табл. 22.1. У ній також вказано заголовки, які необхідно помістити у програму під час використання кожного контейнера. Незважаючи на те, що клас string також є контейнером, який дає змогу зберігати і обробляти символьні рядки, однак він у цю таблицю не включений і розглядається нижче у цьому розділі.

Табл. 22.1. Контейнери, визначені в бібліотеці STL

Контейнер Опис Заголовок
bitset Бітова множина <bitset>
deque Дек (двостороння черга, або черга з двостороннім доступом) <deque>
list Лінійний список <list>
map Відображення. Зберігає пари "ключ-значення", в яких кожен ключ пов'язаний тільки з одним значенням <map>
multimap Мультивідображення. Зберігає пари "ключ-значення", в яких кожен ключ може бути пов'язаний з двома або більше значеннями <map>  
multiset Множина, у якій кожен елемент необов'язково унікальний (мультимножина) <set>  
priority_queue Пріоритетна черга <queue>  
queue Черга <queue>  
set Множина, у якій кожен елемент унікальний <set>  
stack Стек <stack>  
vector Динамічний масив <vector>  

Оскільки імена типів у оголошеннях шаблонних класів довільні, контейнерні класи оголошують typedef -версії цих типів, що конкретизує імена типів. Деякі з найбільш популярних typedef -імен мають таке призначення:

size_type Певний цілий тип, приблизно аналогічний типу size_t
reference Посилання на елемент
const_reference Константне (const-) посилання на елемент
iterator Ітератор
const_iterator Константний (const-) ітератор
reverse_iterator Реверсивний ітератор
const_reverse_iterator Константний реверсивний ітератор
value_type Тип значення, що зберігається у контейнері (те саме, що і узагальнений тип myType)
allocator_type Тип розподільника (пам'яті)
key_type Тип ключа
key_compare Тип функції, яка порівнює два ключі
mapped_type Тип значення, що зберігається у відображенні (те саме, що і узагальнений тип myType)
value_compare Тип функції, яка порівнює два значення

Оскільки в одному розділі неможливо розглянути всі контейнери, в наступних розділах ми представимо тільки три з них: vector, list і map. Якщо Ви зрозумієте, як працюють ці три контейнери, у Вас не буде проблем з використанням інших.

22.3. Робота з векторами

Одним з контейнерів найширшого призначення є вектор. Клас vector підтримує динамічний масив, який у разі потреби може збільшувати свій розмір. Як уже зазначалося вище, у мові програмування C++ розмір масиву фіксується під час компілювання. І хоча це найефективніший спосіб реалізації масивів, він водночас є і тим самим обмежувачем, оскільки розмір масиву не можна змінювати у процесі виконання програми. Ця проблема розв'язується за допомогою вектора, який у міру потреби забезпечує виділення додаткового об'єму пам'яті. Незважаючи на те, що вектор – це динамічний масив, проте, для доступу до його елементів можна використовувати стандартне позначення індексації масивів.

Вектори є динамічними масивами.

Ось як виглядає шаблонна специфікація для класу vector:

template < class myType, class Allocator = allocator <myType>> class vector

У цьому записі myType – тип даних, що зберігається, а елемент Allocator означає розподільник пам'яті, який за замовчуванням використовує стандартний розподільник. Клас vector має такі конструктори:

explicit vector (const Allocator & a = Allocator ());

explicit vector (size_type num|, const myType & val = myType(),

const Allocator & a = Allocator ());

vector (const vector <myType, Allocator > & ob);

template < class InIter > vector (InIter start, InIter end,

const Allocator & a = Allocator ());

Перша форма конструктора призначена для створення порожнього вектора. Друга створює вектор, який містить num "елементів із значенням val ", причомузначення val можебути встановлено за замовчуванням. Третя форма дає змогу створити вектор, який містить ті самі елементи, що і заданий вектор ob. Четвертапризначена для створення вектора, який містить елементи у діапазоні, заданому параметрами-ітераторами start і end.

Заради досягнення максимальної гнучкості та переносності будь-який об'єкт, який призначено для зберігання у векторі, повинен визначатися конструктором за замовчуванням. Окрім того, він повинен визначати операції "<" і "==". Деякі компілятори можуть зажадати визначення і інших операторів порівняння. (З причини існування різних реалізацій для отримання точної інформації про вимоги, що пред'являються Вашим компілятором, необхідно звернутися до документації, що додається до нього.) Всі вбудовані типи автоматично задовольняють ці вимоги.

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

vector < int > iv; // Створення вектора нульової довжини для

// зберігання int-значень.

vector < char > cv(5); // Створення 5-елементного вектора для

// зберігання char -значень.

vector < char > cv(5, 'x'); // Ініціалізація 5-елементного

// char -вектора.

vector < int > iv2(iv); // Створення int-вектора на основі

// int-вектора iv.

Для класу vector визначено такі оператори порівняння: ==, <, <=,!=, > і >=.

Для вектора також визначено оператор індексації "[]", який дає змогу отримати доступ до елементів вектора за допомогою стандартного запису з використанням індексів. Функції-члени, визначені у класі vector, перераховані в табл|. 22.2. Найважливішими з них є size (), begin (), end (), push_back (), insert () і erase (). Дуже корисна функція size (), яка повертає поточний розмір вектора, оскільки вона дає змогу визначити розмір вектора у процесі виконання програми.

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

Табл. 22.2. Функції-члени, визначені у класі vector

Функція-член Опис
template <class InIter> void assign(InIter start, InIter end); Поміщає у вектор послідовність, що визначається параметрами start і end
void assign(size_type num, const myType & val); Поміщає у вектор num елементів із значенням val
reference at(size_type i); const_reference at(size_type i) const; Повертає посилання на елементи, які задаються параметром i
reference back(); const_reference back() const; Повертає посилання на останній елемент у векторі
iterator begin(); const_iterator begin() const; Повертає ітератор для першого елемента у векторі
size_type capacity() const; Повертає поточну місткість вектора, або кількість елементів, яка може зберігатися у векторі до того, як виникне потреба у виділенні додаткової пам'яті
void clear(); Видаляє всі елементи з вектора
bool empty() const; Повертає істинне значення, якщо використовуваний вектор порожній, і помилкове значення інакше
const_iterator end() const; iterator end(); Повертає ітератор для кінця вектора
iterator erase(iterator i); Видаляє елемент, яка адресується ітератором і; повертає ітератор для елемента, розташованого після видаленого
iterator erase(iterator start, iterator end); Видаляє елементи у діапазоні, що задається параметрами start і end; повертає ітератор для елемента, розташованого за останнім видаленим елементом
reference front(); const_reference front() const; Повертає посилання на перший елемент у векторі
allocator_type get_allocator() const; Повертає розподільник пам'яті вектора
iterator insert(iterator i, const myType & val = myType()); Вставляє значення val безпосередньо перед елементом, заданим параметром i; повертає ітератор для цього елемента
void insert(iterator i, size_type num, const myType & val); Вставляє num копій значення val безпосередньо перед елементом, заданим параметром i
template <class InIter> void insert(iterator i, InIter start, InIter end); Вставляє у вектор послідовність елементів, що визначаються параметрами start і end, безпосередньо перед елементом, заданим параметром i
size_type max_size() const; Повертає максимальну кількість елементів, яку може містити вектор
reference operator[]( size_type i) const; const_reference operator[]( size_type i) const; Повертає посилання на елементи, які задаються параметром i
void pop_back(); Видаляє останній елемент у векторі
void push_back(const myType & val); Додає у кінець вектора елемент, значення якого задане параметром val
reverse_iterator rbegin(); const_reverse_iterator rbegin() const; Повертає реверсивний ітератор для кінця вектора
reverse_iterator rend(); const_reverse_iterator rend() const; Повертає реверсивний ітератор для початку вектора
void reserve(size_type num); Встановлює місткість вектора, що дорівнює значенню, не меншому від заданого num
void resize(size_type num, myType val = myType()); Встановлює розмір вектора, що дорівнює значенню, заданому параметром num. Якщо вектор для цього потрібно подовжити, то в його кінець додаються елементи із значенням, що задається параметром val
size_type size() const; Повертає поточну кількість елементів у векторі
void swap(deque<myType, Allocator> & ob); Виконує обмін елементами зухвалого вектора і вектора ob

Функція begin () повертає ітератор, який вказує на початок вектора. Функція end () повертає ітератор, який вказує на кінець вектора. Як вже пояснювалося вище, ітератори подібні до покажчиків, і за допомогою функцій begin () і end () можна отримати ітератори початку і кінця вектора відповідно.

Функція push_back () поміщає задане значення у кінець вектора. У разі потреби довжина вектора збільшується так, щоб він міг прийняти новий елемент. За допомогою функції insert () можна додавати елементи у середину вектора. Окрім того, вектор можна ініціалізувати. У будь-якому випадку, якщо вектор містить елементи, то для доступу до них і їх модифікації можна використовувати засіб індексації масивів. А за допомогою функції erase () можна видаляти елементи з вектора.

Розглянемо короткий приклад, який ілюструє базову поведінку вектора.

Код програми 22.1. Демонстрація базової поведінки вектора

#include <iostream> // Для потокового введення-виведення

#include <vector> // Для роботи контейнерним класом "Вектор"

using namespace std; // Використання стандартного простору імен

 

int main ()

{

vector < int > vek; // Створення вектора нульової довжини

unsigned int i;

 

// Відображаємо початковий розмір вектора vek.

cout << "Розмір = " << vek. size () << endl;

 

/* Поміщаємо значення у кінець вектора, і розмір

вектора за потреби збільшуватиметься. */

for (i=0; i<10; i++) vek. push_back (i);

 

// Відображаємо поточний розмір вектора vek.

cout << "Новий розмір = " << vek. size () << endl;

 

// Відображаємо вміст вектора.

cout << "Поточний вміст:\n";

for (i=0; i<vek. size (); i++) cout << vek[i] << " ";

cout << endl;

 

/* Поміщаємо у кінець вектора нові значення, і розмір

вектора за потреби збільшуватиметься. */

for (i=0; i<10; i++) vek. push_back (i + 10);

 

// Відображаємо поточний розмір вектора vek.

cout << "Новий розмір = " << vek. size () << endl;

 

// Відображаємо новий вміст вектора.

cout << "Новий вміст:\n";

for (i=0; i<vek. size (); i++) cout << vek[i] << " ";

cout << endl;

 

// Змінюємо вміст вектора.

for (i=0; i<vek. size (); i++) vek[i] = vek[i] + vek[i];

 

// Відображаємо вміст вектора.

cout << "Вміст подвоєно:\n";

for (i=0; i<vek. size (); i++) cout << vek[i] << " ";

cout << endl;




Поделиться с друзьями:


Дата добавления: 2014-11-29; Просмотров: 504; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.009 сек.