Студопедия

КАТЕГОРИИ:


Архитектура-(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)

Т Е Х Н О Л О Г І Ї 2 страница




Отже, існує три причини для внесення у мові програмування C++ стандартного класу string несуперечність даних (рядок тепер визначається самостійним типом даних), зручність (програміст може використовувати стандартні С++-оператори) і безпечність (межі масивів відтепер не порушуватимуться). Необхідно мати на увазі, що все вище перераховане не означає, що програміст повинен відмовлятися від використання звичайних рядків, що мають завершальний нуль-символ. Вони, як і раніше, залишаються найбільш ефективним засобом реалізації рядків. Але, якщо швидкість не є для Вас визначальним фак|тором, використання нового класу string дасть Вам доступ до безпечного і повністю інтегрованому способу оброблення рядків.

І хоча клас string традиційно не сприймається як частина бібліотеки STL, він проте, є ще один контейнерний клас, визначено C++ Це означає, що він підтримує алгоритми, описані у попередньому розділі. Dpi цьому рядки мають додаткові можливості. Для отримання доступу до класу string необхідно приєднати до програми заголовок < string >.

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

Прототипи трьох найпоширеніших конструкторів класу string мають ледве дуючий вигляд.

string ();

string (const char *str);

string (const string &str);

Перша форма конструктора створює порожній об'єкт класу string. Друга форма створює string -об'єкт з рядка, що завершується нульовим символом, який адресується параметром str. Ця форма конструктора забезпечує перетворення з рядка, що завершується нульовим символом в об'єкт типу string. Третя створює string -об'єкт з іншого string -об'єкта.

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

Оператор Опис
= Присвоєння
+ Конкатенація
+= Присвоєння з конкатенацією
== Рівність
!= Нерівність
< Менше
< = Менше або рівно
> Більше
> Більше або рівно
[] Індексація
" Введення
" Введення

Ці оператори дають змогу використовувати об'єкти типу string у звичайних виразах і позбавляють програміста від потреби викликати такі функції, як strcpy () або strcpy (). У загальному випадку у виразах можна змішувати string -об'єкти і рядки, що мають завершальний нуль-символ. Наприклад, string -об'єкт можна присвоїти рядку, що завершується нулем.

Оператора додавання "+" можна використовувати для конкатенації одного string -об'єкта з іншим або string -об'єкта з рядком, створеним у С-стіле (С-рядком). Іншими словами, підтримуються наступні операції.

str ing-об'єкт + string -об'єкт

string -об'єкт + С-рядок

С-рядок+ string -об'єкт

Оператор додавання "+" дає змогу також додавати символ у кінець рядка.

У класі string визначена константа прози, яка дорівнює -1. Вона представляє розмір рядка максимально можливої довжини.

Рядковий клас C++ істотно полегшує оброблення рядків. Наприклад, використовуючи string -об'єкти, можна застосовувати оператор присвоєння для призначення string -об'єкту рядка в лапках, оператор додавання "+" для конкатенації рядків і оператори порівняння для порівняння рядків. Виконання цих операцій продемонстровано у наведеному нижче коді програми.

Код програми 22.17. Демонстрація механізму використання класу string для оброблення рядків

#include <vcl>

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

#include <conio> // Для консольного режиму роботи

#include <string> // Для роботи з рядками

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

 

int main ()

{

string str1 ("Клас string дає змогу ефективно ");

string str2("обробляти рядки.");

string str3;

 

// Присвоєння string-об'єкта

str3 = str1;

cout << str1 << "\n" << str3 << "\n";

 

// Конкатенація двох string -об'єктів.

str3 = str1 + str2;

cout << str3 << "\n";

 

// Порівняння string-об'єктів.

if (str3 > str1) cout << "str3 > str1\n";

if (str3 == str1+str2)

cout << "str3 == str1+str2\n";

 

// Об'єкту класу string можна також присвоїти звичайний рядок.

str1 = "Це рядок, що завершується нульовим символом.\n";

cout << str1;

 

// Створення string-об'єкта за допомогою іншого string-об'єкта.

string str4(str1);

cout << str4;

 

// Введення рядка.

cout << "Введіть рядок: "; cin >> str4;

cout << str4;

 

getch (); return 0;

}

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

Клас string дає змогу ефективно

Клас string дає змогу ефективно

Клас string дає змогу ефективно обробляти рядки.

str3 > str1

str3 == str1+str2

Це рядок, що завершується нульовим символом.

Це рядок, що завершується нульовим символом.

Введіть рядок: Привіт

Привіт

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

Важливо також відзначити, що у попередній програмі розмір рядків не задається. Об'єкти типу string автоматично отримують розмір, потрібний для зберігання заданого рядка. Таким чином, у процесі виконання операцій присвоєння або конкатенації рядків рядок-приймач збільшиться по довжині настільки, наскільки це необхідно для зберігання нового вмісту рядка. При обробленню string -об'єктів неможливо вийти за межі рядка. Саме цей динамічний аспект string -об'єктів вигідно відрізняє їх від рядків, що мають завершальний нуль-символ (які часто страждають від порушення меж).

22.7.1. Огляд функцій-членів класу string

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

Необхідно пам'ятати! Оскільки клас string - контейнер, він підтримує такі звичайні контейнерні функції, як begin (), end () і size ().

Основні маніпуляції над рядками

Щоб присвоїти один рядок іншому, використовується функція assign (). Ось як виглядають два можливі формати її реалізації:

string & assign (const string &strob, size_type start size_type num);

string & assign (const char *str, size_type num);

Перший формат дає змогу присвоїти об'єкту, що викликається, num символів з рядка, які задаються параметром strob, починаючи з індексу start. Під час використання другого формату об'єкту, що викликається, присвоюються перші numсимволів рядка, що завершується нульовим символом, які задаються параметром str. У кожному випадку повертається посилання на зухвалий об'єкт. Звичайно, набагато простіше для присвоєння одного повного рядка іншій використовувати оператор "=". Про функцію-члена assign () згадують, в основному, тоді, коли потрібно присвоїти тільки частину рядка.

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

string &append(const string &strob, size_type start size_type num);

string &append(const char *str, size_type num);

У цих записах під час використання першого формату лшп символів з рядка, які задаються параметром strob, починаючи з індексу start, буде приєднано у кінець того, об'єкт, який викликає. Другий формат дає змогу приєднати у кінець об'єкта, що викликається перші пив символів рядка, що завершується нульовим символом, які задаються параметром str. У кожному випадку повертається посилання на зухвалий об'єкт. Звичайно, набагато простіше для приєднання одного повного рядка у кінець іншій використовувати оператор додавання "+". Функція ж append() застосовується тоді, коли необхідно приєднати у кінець об'єкта, що викликається тільки частину рядка.

Вставку або заміну символів у рядку можна виконувати за допомогою функцій-членів класу insert () і replace (). Ось як виглядають прототипи їх найбільш споживаних форматів.

string & insert (size_type start, const string & strob);

string & insert (size_type start, const string & strob,

size_type insStart size_type num|);

string & replace (size_type start size_type num|, const string & strob);

string & replace (size_type start size_type orgNum,

const string &strob,

size_type replaceStart,

size_type replaceNum);

Перший формат функції insert () дає змогу вставити рядок, які задаються параметром strob, у позицію зухвалого рядка, задану параметром start. Другий формат функції insert () призначений для вставки num символів з рядка, які задаються параметром strob, починаючи з індексу insStart, у позицію зухвалого рядка, задану параметром start.

Перший формат функції replace () слугує для заміни лив? символів у рядку, що викликається, починаючи з індексу start, рядком, заданим параметром strob. Другий формат дає змогу замінити orgNum символів у рядку, що викликається, починаючи з індексу start, replaceNum символами рядка, які задаються параметром strob, починаючи з індексу replaceStart. У кожному випадку повертається посилання на зухвалий об'єкт.

Видалити символи з рядка можна за допомогою функції erase (). Один з її форматів має такий вигляд:

string & erase (size_type start = 0 size_type num| = прози);

Ця функція видаляє літ символів із зухвалого рядка, починаючи з індексу start. Функція повертає посилання на зухвалий об'єкт.

Використання функцій insert (), erase () і replace () продемонстровано у наведеному нижче коді програми.

Код програми 22.18. Демонстрація механізму використання функцій insert(), erase() і replace()

#include <vcl>

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

#include <conio> // Для консольного режиму роботи

#include <string> // Для роботи з рядками

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

 

int main ()

{

string str1 ("Це простий тест.");

string str2("ABCDEFG");

cout << "Початкові рядки:\n";

cout << "str1: " << str1 << endl;

cout << "str2: " << str2 << "\n\n";

 

// Демонструємо використання функції insert ().

cout << "Вставляємо рядок str2 у рядок str1:\n";

str1. insert (5, str2);

cout << str1 << "\n\n";

 

// Демонструємо використання функції erase ().

cout << "Видаляємо 7 символів з рядка str1:\n";

str1. erase (5, 7);

cout << str1 <<"\n\n";

 

// Демонструємо використання функції replace ().

cout << "Замінюємо 2 символи в str1 рядком str2:\n";

str1. replace (5, 2, str2);

cout << str1 << endl;

 

getch (); return 0;

}

Результати виконання цієї програми є такими:

Початкові рядки:

str1: Це простий тест.

Str2: ABCDEFG

Вставляємо рядок str2 у рядок str1: Це пАВСВЕРОростой тест.

Видаляємо 7 символів з рядка str1: Це простий тест.

Замінюємо 2 символи в str1 рядком str2: Це пАВСВЕРСстой тест.

Пошук у рядку

У класі string передбачено декілька функцій-членів класу, які здійснюють пошук. Це, наприклад, такі функції, як find () і rfind (). Розглянемо прототипи самих споживаних версій цих функцій.

size_type find (const string & strob,

size_type start = 0) const;

size_type rfind (const string & strob,

size_type start =npos) const;

Функція find (), починаючи з позиції start, проглядає викликаючий рядок на предмет пошуку першого входження рядка, які задаються параметром strob. Якщо пошук успішний, функція find () повертає індекс, за яким у рядку, що викликається, був виявлений збіг. Якщо збігу не виявлено, повертається значення прози. Функція rfind () виконує та ж дія, але з кінця. Починаючи з позиції start, вона проглядає викликаючий рядок у зворотному порядку на предмет пошуку першого входження рядка, які задаються параметром strob (тобто вона знаходить у рядку, що викликається, останнє входження рядка, які задаються параметром strob). Якщо пошук пройшов вдало, функція find () повертає індекс, за яким у рядку, що викликається, був виявлений збіг. Якщо збігу не виявлено, повертається значення прози.

Розглянемо короткий приклад використання функції find ().

Код програми 22.19. Демонстрація механізму використання функції find()

#include <vcl>

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

#include <conio> // Для консольного режиму роботи

#include <string> // Для роботи з рядками

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

 

int main ()

{

int izm;

string s1 = "Клас string полегшує оброблення рядків.";

string s2;

izm = s1. find (" string ");

if (izm!= string::npos) {

cout << "Збіг виявлений у позиції " << izm << endl;

cout << "Залишок рядка такий: ";

s2. assign (s1, izm, s1. size ());

cout << s2;

}

 

getch (); return 0;

}

Програма відображає на екрані такі результати:

Збіг виявлений у позиції 6

Залишок рядка такий: string полегшує оброблення рядків.

Порівняння рядків

Щоб порівняти повний вміст одного string -об'єкта з іншим, звичайно використовуються описані вище перевантажені оператори відношення. Але, якщо потрібно порівняти частину одного рядка з іншою, Вам доведеться використовувати функцію-члена compare ().

int compare (size_type start size_type num,

const string & strob) const;

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

Отримання рядка, що завершується нульовим символом

Незважаючи на незаперечну корисність об'єктів типу string, можливі ситуації, коли Вам доведеться отримувати з такого об'єкта символьний масив, що завершується нулем, тобто його версію С-рядки. Наприклад, Ви могли б використовувати string -об'єкт для створення імені файлу. Але, відкриваючи файл, Вам потрібно задати покажчик на стандартний рядок, що завершується нульовим символом. Для вирішення цього питання і використовується функція-член c_str(). Ось як виглядає її прототип:

const char *c_str() const;

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

22.7.2. Зберігання рядків у інших контейнерах

Оскільки клас string визначає тип даних, можна створити контейнери, які міститимуть об'єкти типу string. Розглянемо, наприклад, вдаліший варіант програми-словника, яку було показано вище.

Код програми 22.20. Демонстрація механізму використання відображення string-об'єктів для створення словника

#include <vcl>

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

#include <conio> // Для консольного режиму роботи

#include <map> // Для роботи з асоціативними контейнерами

#include <string> // Для роботи з рядками

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

 

int main ()

{

map < string. string > dictionary;

 

dictionary. insert (pair < string. string >(

"дім", "Місце мешкання."));

dictionary. insert (pair < string. string >(

"клавіатура", "Пристрій введення даних."));

dictionary. insert (pair < string. string >(

"програмування", "Процес розроблення програми."));

dictionary. insert (pair < string.

string >("STL", "Standard Template Library"));

string s;

cout << "Введіть слово: "; cin >> s;

map < string. string >:: iterator p;

p = dictionary. find (s);

if (p!= dictionary .end ())

cout << "Визначення: " << p-> second;

cout << "Такого слова у словнику немає.\n";

 

getch (); return 0;

}


Розділ 23. Особливості роботи препроцесора С++

Завершальний розділ навчального посібника присвячено особливостям роботи препроцесора C++. Препроцесор C++ – це частина компілятора, яка піддає Вашу програму різним текстовим перетворенням до реальної трансляції початкового коду програми в об'єктний. Програміст може давати препроцесору команди, звані директивами препроцесора (preprocessor directives), які, не будучи формальною частиною мови програмування C++, здатні розширити область дії його середовища програмування. Препроцесор C++ містить наступні директиви.

#define #error #include
#if #else #elif
#endif #ifdef #ifndef
#undef #line #pragma

Як бачите, всі директиви препроцесора починаються з символу "#". Тепер розглянемо кожну з них окремо.

Необхідно пам'ятати! Препроцесор C++ прямий нащадок препроцесора С, і деякі його засоби виявилися надмірними після введення у мові програмування C++ нових елементів. Проте він, як і раніше, є важливою частиною С++-середовища програмування.

23.1. Поняття про директиви препроцесора C++

23.1.1. Директива #define

Директива #define визначає ім'я макросу.

Директива #define використовують для визначення ідентифікатора і символьної послідовності, яка буде підставлена замість ідентифікатора скрізь, де він трапляється у початковому коді програми. Цей ідентифікатор називається макроім'ям, а процес заміни макропідстановкою (реалізацією макророзширення). Загальний формат використання цієї директиви має такий вигляд:

#define макроім'я послідовність_символів

Звернемо Вашу увагу на те, що тут немає крапки з комою. Задана послідовність_символів завершується тільки символом кінця рядка. Між елементами ім'я_макроса і послідовність_символів може бути будь-яка кількість пропусків.

Отже, після внесення цієї директиви кожне входження текстового фрагмента, що є визначеним як макроім'я, замінюється заданим елементом послідовність_символів. Наприклад, якщо виникає бажання використовувати слово UP як значення 1 і слово DOWN як значення 0, оголосіть такі директиви #define.

#define UP 1

#define DOWN 0

Дані директиви змусять компілятор підставляти 1 або 0 кожного разу, коли у файлі початкового коду програми трапиться слово UP або DOWN відповідно. Наприклад, у процесі виконання настанови

cout << UP << " " << DOWN << " " << UP + UP;

на екран буде виведено наступне: 102

Після визначення імені макросу його можна використовувати як частину визначення інших макроімен. Наприклад, наступний програмний код визначає імена ONE, TWO і THREE і відповідні ним значення.

#define ONE I

#define TWO ONE+ONE

#define THREE ONE+TWO

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

#define GETFILE "Введіть ім'я файлу"

//...

Препроцесор замінить рядком "Введіть ім'я файлу" кожне входження ідентифікатора GETFILE. Для компілятора ця cout -настанова

cout << GETFILE;

насправді виглядає так.

cout << "Введіть ім'я файлу";

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

#define GETFILE "Введіть ім'я файлу"

//...

cout << "GETFILE – це макроім'я\n";

на екрані буде відображена ця інформація

GETFILE – це макроім'я

а не ця:

Введіть ім'я файлу – це макроім'я

Якщо текстова послідовність не поміщається на рядку, її можна продовжити на наступній, поставивши зворотну косу межу у кінці рядка, як це показано у наведеному прикладі.

#define LONG _ STRING "Це дуже довга послідовність, \

яка використовується як приклад."

Серед С++-програмістів прийнято використовувати для макроімен прописні букви. Ця домовленість дає змогу з першого погляду зрозуміти, що тут використовується макропідстановка. Окрім того, краще за все помістити всі директиви #define у початок файлу або включити в окремий файл, щоб не шукати їх потім за всією програмою.

Макропідстановки часто використовують для визначення "магічних чисел" програми. Наприклад, у Вас є програма, яка визначає деякий масив, і ряд функцій, які отримують доступ до нього. Замість "жорсткого" кодування розміру масиву за допомогою константи краще визначити ім'я, яке б представляло розмір, а потім використовувати це ім'я скрізь, де повинен знаходитися розмір масиву. Тоді, якщо цей розмір доведеться змінити, Вам достатньо буде внести тільки одну зміну, а потім перекомпілювати програму. Розглянемо такий приклад.

#define Max_size 100

//...

float balance[ Max_size ];

double index[ Max_size ];

int num_emp[ Max_size ];

Необхідно пам'ятати! Важливо пам'ятати, що у мові програмування C++ передбачено ще один спосіб визначення констант, який полягає у використанні специфікатора const. Проте багато програмістів "прийшли" у мові програмування C++ з С-среди, де для цих потреб звичайно використовувалася директива #define. Тому Вам ще часто доведеться з нею стикатися у С++-коді програми.

Макровизначення, що діють як функції

Директива #define має ще одне призначення: макроім'я може використовуватися з аргументами. Під час кожного входженні макроімені пов'язані з ним аргументи замінюються реальними аргументами, вказаними у коді програми. Такі макровизначення діють подібно до функцій. Розглянемо такий приклад.




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


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


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



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




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