Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Зубенко, Омельчук - Програмування. Поглиблений курс

.pdf
Скачиваний:
58
Добавлен:
07.03.2016
Размер:
5 Мб
Скачать
☆

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

Мова С++ була створена Б. Страуструпом у 1979 р. як об'єктно- орієнтований варіант мови C. Надалі обидві мови розвивалися, впли- ваючи одна на одну. У 1998 р. був прийнятий ANSI/ISO-стандарт мо- ви С++, побудований на основі C89, а в 1999 р. – новий ANSI/ISO- стандарт для мови C – C99. Ця версія мала нові можливості, деякі з них були запозичені зі стандарту C++ 1998 р., а деякі – нові. У C99 є незначна кількість конструкцій, не сумісних зі стандартом C++ 1998 р. Опис їх можна знайти в [133]. Якщо їх відкинути, то отрима- ємо власно С, що дає можливість писати С-програми таким чином, щоб вони задовольняли стандарти обох мов С/C++. Отже, мову С++ умовно можна розділити на три частини:

С++ = С + додаткові (не ООП) можливості + ООП

Розпочнемо розгляд мови С++ із другої частини, тобто з нових, не об'єктно-орієнтованих, її можливостей.

3.10.1. СТАНДАРТНІ БІБЛІОТЕКИ С++

Як і в C, у C++ немає операцій, що забезпечують введення-виведення, роботу з рядками, здійснюють математичні розрахунки тощо. Усі ці опе- рації виконуються за рахунок використання набору бібліотечних функ- цій, що підтримуються компілятором. У C++ існують два основні типи бібліотек: бібліотеки С та C++. Бібліотеки в C++, як і в C, активізуються за допомогою директиви препроцесора #include, але їхні імена не мають розширення .h. Однак його додання не викличе помилки.

Значна частина бібліотеки C++ заснована на бібліотеці шаблонів

STL (англ. – Standard Template Library). Вона надає такі важливі засо-

би, як контейнери – вектори (std::vector<>), списки (std::list<>),

стеки (std::stack<>), черги (std::queue<>), рядки (std::string),

множини (std::set<>) тощо – та ітератори, що дають доступ до кон- тейнерів та інших інструментів. Детальніше з рядками можна озна- йомитися нижче, а з контейнерними типами – у підрозд. 4.3.5.

У стандартній C++ уся інформація, пов'язана зі стандартною біблі- отекою, визначена в просторі імен std. Для отримання прямого до- ступу до елементів стандартної бібліотеки потрібно перед назвою еле- мента зазначати ім'я простору, наприклад std::cout, або після вклю- чення потрібного заголовку використати оператор using:

using namespaсe std;.

Зауважимо, що деякі застарілі компілятори C++ не підтримують команду namespace і заголовки без зазначення розширення. При ви-

421

ПРОГРАМУВАННЯ

користанні заголовка в традиційному стилі C усі імена, визначені цим заголовком, розміщуються в глобальному просторі імен, а не в прос- торі std, тому оператор using не потрібен.

3.10.2. ОБЛАСТЬ ДІЇ ЗМІННИХ

У мові C++ і версії C99 локальні змінні можна оголошувати практи- чно в довільному місці блока, головне, щоб змінна була оголошена пе- ред її використанням:

int f()

{

int x=10; x++;

int n=100;

for(int i=0; i<n; i++)

{

x+=n*i;

}

return x;

}

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

3.10.3. ПРОСТІР ІМЕН

У мові C++ можна створювати простори імен оператором namespace. Простір імен визначає деяку декларативну область, що обмежує дію імен. Простори імен потрібні для уникнення колізій між пакетами, які мають збіжні імена глобальних змінних, функцій і типів:

<простір-імен>::=namespace <ім'я_простору_імен> { <фрагмент-програми>

}

На імена, оголошені всередині деякого простору імен, можуть на- пряму посилатися інші оператори всередині цього простору.

Приклад 3.75. Робота з простором імен:

namespace МуNamespaсe { const int a=5;

422

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

int count; typedef int**T;

void f(x) {return a*x};

...

}

Поза простором імен для звернення до змінної додається ім'я простору:

МуNamespaсe::a, МуNamespaсe::count, МуNamespaсe::T, МуNamespaсe::f.

Якщо в деякому файлі потрібно звернутися до змінних, визначених у іншому просторі імен, то використовують оператор using:

using namespace МуNamespaсe; /*використання простору МуNamespase*/

using МуNamespaсe::T; /*використання змінної T із простору МуNamespase*/

■

Спеціальним випадком є безіменний простір імен. Усі імена, описані в ньому, доступні в поточному файлі й більше ніде:

namespace {

...

}

3.10.4. СПЕЦИФІКАТОРИ КЛАСІВ ПАМ'ЯТІ

Мовою C++ успадковані від мови C такі специфікатори класів па- м'яті: extern, auto, register, static, що використовуються для зміни способу створення пам'яті для змінних. У мові C++ при використанні специфікатора static для даних – членів класу – створюється лише одна копія цих членів, яка сумісно використовується всіма об'єктами. До того ж у C++ з'явився новий специфікатор mutable, що дозволяє специфікованому таким чином атрибуту об'єкта, кваліфікованому як const, бути модифікованим.

3.10.5. ВВЕДЕННЯ-ВИВЕДЕННЯ В МОВІ C++

Частиною стандартної бібліотеки C++ є бібліотека <iostream>, що реалізована як ієрархія класів і забезпечує базові можливості введен- ня-виведення. Для її використання потрібна директива #include <iostream> і відкриття доступу до відповідного простору імен: using namespaсe std.

423

ofstream,

ПРОГРАМУВАННЯ

Введення з клавіатури називається стандартним введенням і прив'язане до об'єкта cin. Стандартне виведення на екран прив'язане до об'єкта cout. Стандартне виведення помилок прив'язане до об'єк-

та cerr. У мові С замість них використовуються стандартні потоки stdіn, stdout та stderr. Щоб значення потрапило в стандартний по- тік, використовується операція виведення <<. Наприклад:

int i_x=10; cout<<"i_x=="<<i_x<<'\n';

Замість символу '\n' можна використовувати стандартний опе- ранд endl, що виводить символ переходу на новий рядок і здійснює дозаписування решти символів із буфера виведення:

int i_x=10; cout<<"i_x=="<<i_x<<endl;

Для читання значення зі стандартного потоку застосовується опе- рація введення >>. Наприклад:

string name; int age;

…

cout<<"Введіть ім'я та вік:"; cin>>name>>age;

Файлове введення-виведення. Бібліотека iostream підтримує та-

кож файлове введення й виведення (аналогічно перенаправленню потоків у C). Для використання файлів потрібно підключити ще одну бібліотеку:

#include <fstream>.

Для роботи з файлами використовуються такі класи:

ifstream, fstream.

Перед відкриванням файла для виведення потрібно оголосити об'-

єкт типу ofstream:

/*потік виведення, відкритий у режимі за умовчанням*/ ofstream outfile("temp.out");

Файл типу ofstream за умовчанням відкривається в режимі виве- дення ios_base::out або режимі дозаписування ios_base::app:

/*потік виведення, відкритий у режимі дозаписування*/ ofstream outfile("temp.out", "ios_base::app");

424

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

Перед відкриванням файла для введення потрібно оголосити об'єкт типу ifstream:

ifstream outfile("temp.in");

Для перевірки коректності відкривання файла перевіряємо об'єкт outfile:

if(!outfile) {//файл не відкрито

…

}

Для закривання файла потрібно викликати функцію (член класу)

close().

Об'єкти класів ofstream та ifstream можна визначати без зазна- чення імені файла. Пізніше до цього об'єкта можна приєднати ім'я файла функцією open():

ifstream f1;

…

f1.open("file1");

…

f1.close();

Об'єкт класу fstream може відкривати файл для введення чи виве- дення. Наприклад, відкриємо файл для введення й дозаписування:

fstream f2("file2", ios_base::in|ios_base::app);

Об'єкт цього класу можна позиціонувати за допомогою методів seekg() (для читання) чи seekp() (для запису).

Для позиціонування у фіксовану позицію у файлі використовується формат команди

seekg(pos_type position);

Для зміщення відносно поточної позиції використовується формат команди

seekg(off_type position, ios_base::seekdir dir);

Тут dir може набувати таких значень: ios_base::beg – від початку файла; ios_base::cur – від поточної позиції; ios_base::end – від кінця файла.

425

ПРОГРАМУВАННЯ

Приклад 3.76. Позиціонування:

fstream f2("file2", ios_base::in|ios_base::app); f2.seekp(4); /*позиціонування на четвертий символ*/

f2.seekp(10, ios_base::beg); /*зміщення на 6 символів від поча-

тку файла*/

■

Розглянемо приклад програми, що працює з файлами.

Приклад 3.77.

Лістинг.

#include <iostream> #include <fstream> #include <string> using namespace std; int main()

{

int i;

ifstream infile("c:\\TEMP\\file1.txt"); ofstream outfile("c:\\TEMP\\file2.txt"); if((!infile)||(!outfile))

{

cerr<<"Error.\n"; return -1;

}

string word; while(infile>>word)

outfile<<word<<' '; outfile.close(); infile.close();

fstream iofile("c:\\TEMP\\file3.txt"); if(!iofile)

{

cerr<<"Error.\n"; return -1;

}

iofile.seekp(6, ios_base::beg); iofile<<"!!!!"<<' ';

iofile.seekp(6, ios_base::end); iofile<<"????"<<' '; iofile.close();

426

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

return 0;

}

Значення файлів:

C:\temp\file1.txt aaaaaaa

bbbb c dddd eeeeeee fff ggg

C:\temp\file2.txt

aaaaaaa bbbb c dddd eeeeeee fff ggg

C:\temp\file3.txt /*до запуску програми*/

слово1 cлово2 слово3 слово4 слово5

C:\temp\file3.txt /*після виконання програми*/ слово1???? о2 слово3 слово4 слово5

■

3.10.6. РОБОТА З ФУНКЦІЯМИ В C++

Прототипи функцій. У мові C++ усі функції повинні мати прототип. У C99 для задання прототипу функції, що не має параметрів, замість їхнього списку використовується порожній тип void, наприклад int f(void);. У C++ уживання void у даному випадку не обов'язкове (доз- волене, але зайве). Якщо функція поряд із фіксованими параметрами має й нефіксовані, то потрібно користуватися, як і в С, трьома крапка-

ми. Наприклад: int printf(const char* fmt, ...);

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

int sum(int x1, int x2, int x3, int x4, int x5)

{

return x1+x2+x3+x4+x5;

}

Для виклику функції лише з трьома аргументами потрібно допов- нити нулями ті аргументи, що не використовуються:

int x=2, y=3, z=10, rez ; rez=sum(x, y, z, 0, 0);

427

ПРОГРАМУВАННЯ

Такий виклик функції виглядає неприродним. Ситуація спроститься, якщо в прототипі функції записати

int sum(int x1, int x2, int x3=0, int x4=0, int x5=0);

Тоді ми можемо використовувати виклики:

int rez;

rez=sum(2, 3, 10); /* x1==2, x2==3, x3==10, x4==0, x5==0*/ rez=sum(2, 3); /* x1==2, x2==3, x3==0, x4==0, x5==0*/ rez=sum(2, 3, 10, 4); /* x1==2, x2==3, x3==10, x4==4, x5==0*/ rez=sum(2, 3, 10, 4, 5); /* x1==2, x2==3, x3==10, x4==4,

x5==5*/

Вбудовані функції. Як і мова С, C++ містить вбудовані функції inline. Оголошуючи вбудовану функцію, ми даємо команду компілятору там, де це можливо, вставляти тіло функції безпосередньо

вмісце її виклику. Методи класів також можуть бути вбудованими. Приклад 3.78. Опис вбудованої функції.

Лістинг.

#include <iostream>

using namespace std; /*робить видимими імена простору std, зок-

рема cout*/

inline int max(int a, int b); int main()

{

int x, y, z; cout<<"Input x:"; cin>>x; cout<<"Input y: "; cin>>y;

z=max(x, y); cout<<"max("<<x<<","<<y<<")=="<<z<<endl; return 0;

}

//команда компілятору за можливості //вбудувати функцію в місце виклику inline int max(int a, int b)

{

if(a>b) return a; else return b;

}

■

428

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

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

Наведемо простий приклад програми для демонстрації того, як пе- ревантаження функцій спрощує текст програми.

Приклад 3.79. Перевантаження функцій.

Лістинг.

#include <iostream>//для cout

using namespace std; /*робить видимими імена простору std, зок-

рема cout*/

int cube(int a); double cube(double a);

int main()

{

int i_x=5; double d_x=10.2;

cout<<cube(i_x)<<"\n"; cout<<cube(f_x)<<"\n"; return 0;

}

int cube(int a)

{

return a*a*a;

}

double cube(double a)

{

return a*a*a;

}

■

3.10.7. КОНСТАНТИ Й ТИПИ ДАНИХ МОВИ C++

У мові C++, як і в C, виділяють базові й похідні типи даних. Деякі типи даних мови C++ успадковані від C, тому тут вони розглядатися не будуть. Розглянемо нові типи даних мови C++ і зміни у визначенні типів, запозичених із C.

429

ПРОГРАМУВАННЯ

bool. У C++ визначено дві булеві константи: true та false, а також додано новий тип bool, що має значення true та false. Операції порі- вняння повертають тип bool. Зокрема, предикати після if, while зво- дяться до типу bool. Зауважимо, що у версії C99 мови C введено ана- логічний тип під назвою _Bool, але не визначено булеві константи

(у <stdbool.h> визначено макроси true та false).

Рядки. У С++ підтримуються два типи рядків: вбудований тип, ус- падкований від C – символьні масиви char[] та wchar_t[] для ши- роких символів і, відповідно, класи string та wstring зі стандартної бібліотеки C++. Насправді класи string та wstring інстанційовані з шаблонного класу basic_string оголошеннями

typedef basic_string<char> string: typedef basic_string<wchar_t> wstring:

C++-рядки визначені в бібліотеці <string>. Усі імена, визначені в ній, входять до стандартного простору імен std.

У класі string є кілька конструкторів. Наведемо приклади ство- рення об'єктів класу string різними способами:

string str; //створює порожній рядок

string str(cstr); //створює рядок за C-рядком

string str(cstr, len); //створює рядок із len символів C-рядка string str(num, ch); //створює рядок із num символів ch string str(s); //створює рядок-копію за рядком s

string str(s, ind); /*створює рядок із символів рядка s, починаючи з індексу ind*/

string str(str, ind, len); /*створює рядок символів рядка str,

починаючи з індексу ind, довжина створюваного рядка не більше len*/ string str(beg, end, s); /*створює рядок з інтервалу [beg, end)

символів рядка s*/

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

Операція

Дія

=

Присвоювання. Визначено три типи присвоювання: рядку

 

присвоюється значення C++-рядка; значення C-рядка;

 

значення символу

+

Конкатенація

==

Рівність

!=

Нерівність

<

Менше

<=

Менше чи дорівнює

>

Більше

>=

Більше чи дорівнює

430