МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ “ЛЬВІВСЬКА ПОЛІТЕХНІКА”
Кафедра ЕОМ
"Перетворення типів даних"
МЕТОДИЧНІ ВКАЗІВКИ
до лабораторної роботи № 3
з дисципліни
" Програмування. Частина III.
Структури даних та алгоритми "
для студентів напряму
6.050102 “Комп’ютерна інженерія”
Львів – 2009
Методичні вказівки до лабораторної роботи "Перетворення типів даних" з дисципліни “Програмування. Частина IIІ. Структури даних та алгоритми" для підготовки студентів напряму 6.050102 “Комп’ютерна інженерія” / Укл. Т.А.Лисак, Л.О.Цигилик – Львів: Видавництво НУ “Львівська політехніка”, 2009 – 27 с.
Укладач: Лисак Т.А., ст. викладач каф.ЕОМ
Цигилик Л.О., асистент каф.ЕОМ
Відповідальний
за випуск: Мельник А.О., д-р техн. наук, проф.
Рецензенти: Мороз І.В., ст. викладач каф.ЕОМ
Юрчак І.Ю., доцент кафедри САПР, к.т.н.
1. МЕТА РОБОТИ
Дослідження методів та засобів явного та неявного перетворення типів даних.
2. ТЕОРЕТИЧНІ ВІДОМОСТІ
Перетворення типів
Обчислення значень виразів в операторах мови C++ забезпечується виконанням операцій і викликом функцій. Операції використовують операнды, функції вимагають параметрів. Операнды і параметри характеризуються типами. У мові C++ не існує операцій, що, наприклад, забезпечували б додавання або множення операндів різних типів. Частину проблем, пов'язаних з узгодженням типів операндів і параметрів транслятор бере на себе. Фактично це означає введення ще однієї системи правил, що називається правилами стандартного перетворення типів. Перетворення типів мають місце в присвоюваннях, cast - операціях, викликах функцій і при виконанні операцій.
Якщо в виразах зустрічаються операнды різних типів, то вони перетворюються до загального типу відповідно до деякого набору правил. Загалом, автоматично здійснюються тільки такі перетворення, що мають зміст, такі, наприклад, як перетворення цілого в дійсне. Вирази ж, позбавлені змісту, такі, наприклад, як використання змінної типу float в масивах як індекса, заборонені. Нехай, наприклад, потрібно виконати таку операцію:
int ix = 0;
ix = 5.74 + 1; // зазвичай компілюється з попередженням
В цьому прикладі додаються літерали різних типів: 5.74 типу double та 1 типу int. Мова програмування C++ не може безпосередньо додати подібні операнди, спочатку відбувається зведення їх до одного типу. Для цього існують правила перетворення арифметичних типів. Загальний принцип такий: перейти від операнда меншого типу до більшого, щоб не втратити точності обчислень. В наведенному прикладі ціле значення 1 трансформується в тип double, і тільки після цього відбувається додавання. Таке перетворення виконується незалежно від бажання програміста, відтак воно і отримало назву неявного перетворення типу. Результат додавання двох чисел типу double теж має тип double. В прикладі це значення рівне 6.74. Тепер його необхідно присвоїти змінній ix. Тип змінної ix і тип результату обчислень 6.74 не співпадають, отже, тип результату буде приведений до типу змінної, тобто до типу int. Перетворення double в int відбувається автоматично, шляхом відкидання дробової частини (а не заокругленням її). Таким чином, 6.74 перетворюється в 6, і даний результат присвоюється змінній ix. При такому перетворенні може виникати втрата точності, тому більшість компіляторів видають попередження.
Оскільки компілятор не заокруглює числа при перетворенні double в int, то при необхідності програміст повинен робити це сам, наприклад:
double dx = 5.74;
int ix = 1;
ix = dx + ix + 0.5; // перетворення із заокругленням
В цьому прикладі змінна іх набуває значення 7.
За бажанням можна здійснити явне перетворення типів:
ix = static_cast< int >(5.74) + 1;
В цьому прикладі явно вказується компілятору привести величину 5.74 до типу int, а не додержуватись правил за замовчуванням.
2.1. Неявне перетворення типів
Мова програмування С++ встановлює набір стандартних перетворень між об’єктами різних вбудованих типів, що неявно виконуються компілятором в таких випадках:
Використання в арифметичному виразі операндів з різними типами.
У цьому випадку типи всіх операндів приводяться до типу одного операнда, розмір якого є найбільшим (це називається арифметичним перетворенням), наприклад:
int ix = 1;
double dx = 5.74;
if ix + dx {…}; // змінна ix перетворюється в тип double зі значенням 1.0
Присвоювання значення виразу одного типу об’єкту іншого типу.
У цьому випадку результуючим буде тип об’єкта, якому присвоюється значення, наприклад:
ix = dx; // dx зі значенням 5.74 перетворюється в int зі значенням 5
Передача функції аргумента, тип якого відрізняється від типу відповідного формального параметра.
У цьому випадку тип фактичного аргумента приводиться до типу формального параметра, наприклад:
extern double sqrt( double );
cout << "Квадратний корінь з 2 = " << sqrt( 2 ); // 2 перетворюється в double 2.0
Повернення з функції значення, тип якого не співпадає з типом результату, заданим в оголошенні функції.
У цьому випадку тип значення, що фактично повертається, приводиться до оголошеного, наприклад:
double fun ( int ix1, int ix2 ){
return ix1 – ix2; // результат перетворюється в double
}
2.1.1. Арифметичне перетворення типів
Неявні арифметичні перетворення, як правило, здійснюються природнім шляхом. У загальному випадку, арифметичне перетворення приводить обидва операнди бінарного арифметичного виразу до одного типу, який і буде типом результату виразу. Існують два загальних правила:
Типи операндів завжди приводяться до того з типів, що здатний забезпечити найбільший діапазон значень при найбільшій точності. Це допомагає зменшити втрати точності при перетворенні.
Будь-який арифметичний вираз, що включає в себе цілі типів операндів, менші ніж іnt, перед обчисленням завжди перетворює їх в іnt.
Сформулюємо більш точно ієрархію правил перетворень починаючи з найбільшого типу long double. Ці правила називаються звичайними арифметичними перетвореннями:
Якщо будь-який з операндів має тип long double, то й інший приводиться до long double.
У противному випадку, якщо будь-який з операндів має тип double, то й інший приводиться до double.
У противному випадку, якщо будь-який з операндів має тип float, то й інший приводиться до float.
У противному випадку для обох операндів здійснюється цілочисельне підвищення, а саме якщо один з операндів має тип unsіgned long іnt, то й інший перетвориться в unsіgned long іnt.
У противному випадку, якщо один з операндів належить типові long іnt, а інший - unsіgned іnt, то результат залежить від того, чи покриває long іnt всі значення unsіgned іnt, і якщо це так, то unsіgned іnt приводиться до long іnt; якщо ні, то обидва операнди перетворяться в unsіgned long іnt.
У противному випадку, якщо один з операндів має тип long іnt, то й інший приводиться до long іnt.
У противному випадку, якщо один з операндів має тип unsіgned іnt, то й інший приводиться до unsіgned іnt.
У противному випадку обидва операнди мають тип іnt.
Приклад 1.
Нехай задано такі оголошення:
int ix;
char cх;
bool bx;
long lx;
unsigned long ulx;
unsigned int uix;
float fx;
double dx;
Розглянемо вирази:
1). 3.14159L + 'a'
Символьна константа 'a' (значення 97 за таблицею ASCII) приводиться до long double і потім додається до літерала 3.14159L того ж типу.
2). dx + fх + ix
В цьому прикладі змінні fх і iх перетворюються в double перед додаванням .
3). cх + fх + ix;
В даному випадку намає операндів типу double і long double, але є операнд типу float, тип решти операндів змінюється на float.
4). Якщо у виразі намає дійсних операндів, то всі вони являють собою цілі типи. Перш ніж визначити тип результату, виконується перетворення, яке називається приведенням до цілого: всі операнди з типами меньшими, ніж int, заміняються на int. Наприклад:
enum status { bad, ok };
В цьому переліку значення елементів рівні 0 і 1. Обидва ці значення можуть бути представлені типом char, значить char і має стати типом внутрішнього представлення даного переліку. Приведення до цілого типу перетворює char в int.
5). При приведені до цілого типи char, signed char, unsigned char та short int перетворюються в int. Тип unsigned short int трансформується в int, якщо даного типу достатньо для представлення всього діапазону значень unsigned short int (зазвичай це відбувається в системах, які відводять півслова під short та ціле слово під int), в іншому випадку unsigned short int замінюється на unsigned int. Наприклад, в наступному виразі:
cx + ulx
перед визначенням типу результату змінна cх перетворюється в тип int. Після приведення до цілого порівнюються типи операндів. Один із них має тип unsigned long, отже інший буде також цього типу.
6). cx + uix + 1024 + lx
В цьому прикладі змінна cх, змінна uix і константа 1024 перетворюються в long перед додаванням. Як зазначалось вище, з цього правила існує одне виключення: перетворення unsigned int в long відбувається тільки в тому випадку, якщо тип long здатний вмістити весь діапазон значень unsigned int. Зазвичає це не так в 32-бітних системах, де і long, і int представляються одним машинним словом. Якщо ж тип long не здатний представити весь діапазон unsigned int, тоді обидва операнди зводяться до типу unsigned long. Отже, цей арифметичний вираз буде мати тип unsigned long на 32-розрядних комп’ютерах.
На закінчення, ще раз наголосимо основну ідею: арифметичне перетворення типів ставить своєю метою зберегти точність при обчисленні. Це досягається шляхом приведення типів всіх операндів до найбільшого типу, здатному вмістити будь-яке значення кожного з присутніх у виразі операндів.
2.1.2. Перетворення типів при присвоюванні
В операціях присвоювання тип значення, що присвоюється, перетворюється до типу змінної, що одержує це значення. У мові С++ допускаються перетворення при присвоюванні між цілими і дійсними типами, навіть у випадку, коли перетворення спричиняє втрату інформації.
Перетворення знакових цілих типів
Методи виконання перетворень залежать від типів. Наприклад, знакове ціле (sіgned іnt) перетворюється до короткого знакового цілого (short sіgned іnt) за допомогою усікання старших бітів. Коротке знакове ціле (short sіgned іnt) перетворюється до довгого знакового цілого (long sіgned іnt) шляхом розмноження знака вліво. Перетворення знакових цілих (sіgned іnt) до дійсних величин, відбувається без втрати інформації, за винятком можливої втрати деякої точності, коли перетворюються величини long у float. При перетворенні знакового цілого (sіgned іnt) до беззнакового цілого (unsіgned іnt), знакове ціле перетворюється до розміру беззнакового цілого і результат інтерпретується як беззнакова величина.
Таблиця 1. Перетворення знакових цілих типів.
Перетворення
Метод
з типу
в тип
char
Short
доповнення знаком
char
Long
доповнення знаком
char
unsigned char
збереження бітів, старший біт втрачає функцію знакового біта
char
unsigned short
доповнення знаком до short, перетворення short в unsigned short
char
usigned long
доповнення знаком до long, перетворення long в unsigned long
char
Float
доповнення знаком до long, перетворення long в float
char
double
доповнення знаком до розміру double, перетворення в double
short
Char
збереження молодшого байта
short
Long
розмноження знака
short
unsigned char
збереження бітів молодшого байта, старший біт втрачає функцію знакового біта
short
unsigned long
доповнення знаком до long, перетворення long в unsigned long
short
unsigned short
збереження бітів, старший біт втрачає функцію знакового біта
short
Float
доповнення знаком до long, перетворення long к float
short
double
доповнення знаком до розміру double, перетворення в double
long
Char
збереження молодшого байта
long
Short
збереження двох молодших байтів
long
unsigned char
збереження бітів молодшого байта, старший біт втрачає функцію знакового біта
long
unsigned short
збереження двох молодших байтів, старший біт втрачає функцію знакового біта
long
unsigned long
збереження бітів, старший біт втрачає функцію знакового біта
long
Float
представляється як float, якщо long не може бути представлене точно, то відбувається деяка втрата точності
long
double
представляється як double, якщо long не може бути представлене точно, то відбувається деяка втрата точності
Зауваження: тип іnt еквівалентний або типові short, або типові long у залежності від реалізації. Перетворення значень типу іnt відбувається як для типу short або як для типу long, в залежності від того, що підходить.
Перетворення беззнакових цілих типів
Беззнакове ціле перетворюється до короткого беззнакового або знакового цілого шляхом усікання старших бітів. Беззнакове ціле перетвориться до довгого беззнакового або знакового цілого шляхом розмноження нуля. Беззнакові цілі перетворюється до дійсних величин шляхом перетворення до найближчого знакового цілого того ж самого розміру, а потім перетворення цієї знакової величини до дійсної величини. Коли беззнакове ціле перетвориться до знакового цілого того ж розміру, то стан бітів не міняється. Однак, значення цього представлення зміниться, якщо був встановлений знаковий біт.
Таблиця 2. Перетворення беззнакових цілих типів.
Перетворення
Метод
з типу
в тип
unsigned char
chart
збереження всіх бітів, старший біт стає знаковим
unsigned char
short
доповнення нулем
unsigned char
long
доповнення нулем
unsigned char
unsigned short
доповнення нулем
unsigned char
unsigned long
доповнення нулем
unsigned char
float
перетворення до long, перетворення long в float
unsigned char
double
перетворення до long, перетворення long в double
unsigned short
char
збереження молодшого байта
unsigned short
short
збереження всіх бітів, старший біт стає знаковим
unsigned short
long
доповнення нулем
unsigned short
unsigned char
збереження молодшого байта
unsigned short
unsigned long
доповнення нулем
unsigned short
float
перетворення до long, перетворення long в float
unsigned short
double
перетворення до long, перетворення long в double
unsigned long
char
збереження молодшого байта
unsigned long
short
збереження двох молодших байтів
unsigned long
long
збереження всіх бітів, старший біт стає знаковим
unsigned long
unsigned char
збереження молодшого байта
unsigned long
unigned short
збереження двох молодших байтів
unsigned long
float
перетворення до long, перетворення long в float
unsigned long
double
перетворення до long, перетворення long в double
Зауваження: тип unsigned int еквівалентний або типові unsigned short, або типові unsigned long у залежності від реалізації. Перетворення значень типу unsigned іnt відбувається як для типу unsigned short або як для типу unsigned long, в залежності від того, що підходить.
Перетворення дійсних типів
Величини float перетворюється до double, не міняючись у значенні. Величини double, перетворені до float, представляються точно, якщо це можливо. Якщо значення занадто велике для float, то точність губиться. Дійсні типи перетворюється до цілих типу long. Перетворення до інших цілих типів виконується як для long. Дробова частина дійсної величини відкидається при перетворенні до long; якщо результат занадто великий для long, то результат перетворення невизначений.
Таблиця 3. Перетворення дійсних типів.
Перетворення
Метод
з типу
в тип
float
Char
перетворення до long, перетворення long в char
float
Short
перетворення до long, перетворення long в short
float
Long
усікання дробової частини; результат невизначений, якщо він занадто великий для представлення в long
float
unsigned short
перетворення до long, перетворення long в unsigned short
float
unsigned long
перетворення до long, перетворення long в unsigned long
float
Double
зміна внутрішнього представлення
double
Char
перетворення до float, перетворення float в char
double
Short
перетворення до float, перетворення float в short
double
Long
усікання дробової частини; результат невизначений, якщо він занадто великий для представлення в long
double
unsigned short
перетворення до long, перетворення long в unsigned short
double
unsigned long
перетворення до long, перетворення long в unsigned long
double
Float
представляється як float; якщо значення double не може бути точно представлено як float, то точність губиться; якщо значення занадто велике для представлення як float, то результат невизначений
Перетворення адресних типів
Вказівник на величину одного типу може бути перетворений до вказівника на величину іншого типу. Результат може бути, однак, невизначеним через відмінність у вимогах до вирівнювання і розмірів пам'яті.
У деяких реалізаціях є спеціальні ключові слова near, far, huge, що модифікують розмір вказівників у програмах.
Значення вказівника може бути перетворене до цілої величини. Шлях перетворення залежить від розміру вказівника і розміру цілого типу наступним чином:
- якщо вказівник має той самий або менший розмір, чим цілий тип, то вказівник перетвориться точно так само як беззнакове ціле, за винятком того, що він не може бути перетворений до дійсної величини;
- якщо розмір вказівника менший ніж розмір цілого типу, то вказівник спочатку перетворюється до вказівника з тим самим розміром, що й цілий тип, і потім перетворюється до цілого типу. Метод перетворення вказівника до більш довгого вказівника залежить від реалізації.
Цілий тип може бути перетворений до адресного типу. Якщо цілий тип того ж самого розміру, що й адресний, то виконується просте перетворення до виду вказівника (беззнакового цілого). Якщо розмір цілого типу відмінний від розміру адресного типу, то цілий тип спочатку перетворюється до розміру вказівника, використовуючи методи перетворення даних, і потім отримане значення представляється як вказівник.
Цілочисельний константний вираз зі значенням 0 або він же, але приведений до типу voіd *, може бути перетворений у вказівник будь-якого типу операторами приведення, присвоювання і порівняння. Результатом буде NULL-вказівник, що дорівнює будь-якому іншому NULL-вказівникові того ж типу, але не дорівнює ніякому вказівникові на реальний об'єкт або функцію.
Вказівник може також перетворюватися в voіd * і назад, значення вказівника при цьому не змінюється.
Вказівник може неявно перетворюватися в значення типу bool, при цьому ненульовий вказівник перетвориться в true, а нульовий у false.
Присвоювання вказівників допускається в двох випадках:
вказівникам типу voіd*:
якщо тип вказівників праворуч і ліворуч від операції присвоювання той самий.
Таким чином, неявне перетворення виконується тільки до типу voіd*. Значення 0 неявно перетворюється до вказівника на будь-який тип.
Перетворення інших типів
Згідно з визначенням типу enum випливає, що величини enum є величинами типу іnt. Тому перетворення в тип enum або з типу enum здійснюється так само, як для іnt-типів.
Не припустимі перетворення об’єктів типу структура або об'єднання.
2.2. Явне перетворення типів
Явне перетворення типів здійснюється за допомогою наступних cast-операторів:
static_cast, dynamic_cast, const_cast та reinterpret_cast.
Зауважимо, що, хоча іноді явне перетворення типів є необхідним, воно служить потенційним джерелом помилок, оскільки відміняє перевірку типів, що виконується компілятором. Отже, навіщо потрібно таке перетворення.
Вказівник на об'єкт будь-якого неконстантного типу може бути присвоєний вказівнику типу voіd*, що використовується в тих випадках, коли дійсний тип об'єкта або невідомий, або може мінятися в ході виконання програми. Тому вказівник voіd* іноді називають універсальним вказівником. Наприклад:
int ix;
int *pi = 0;
char *pc = 0;
void *pv;
pv = pi; // правильно: неявне перетворення
pv = pc; // правильно: неявне перетворення
const int *pci = &ix;
pv = pci; // помилка: pv іншого типу ніж const void*;
const void *pcv = pci; // правильно
Однак вказівник void* не може бути безпосередньо розіменований. Компілятор не знає типу об’єкта, який адресується даним вказівником, проте це знає програміст, який хоче перетворити вказівник void* на вказівник певного типу. С++ не забезпечує подібного автоматичного перетворення. Наприклад:
#include <cstring>
int ix = 1;
void *pv;
int *pi = &ix;
const char *pc = "PROGRAM";
void main() {
pv = pi; // правильно: pv отримує адресу змінної ix
pc = pv; // помилка: немає стандартного перетворення
char *pstr = new char[ strlen( pc )+1 ];
strcpy( pstr, pc );
}
Компілятор видає повідомлення про помилку, тому що в даному випадку вказівник pv містить адресу цілого числа ix, і саме цю адресу намагаються присвоїти вказівнику на рядок символів. Якби така програма була допущена до виконанння, то виклик функції strcpy(), що очікує на вході рядок символів з нулем наприкінці , швидше за все привів би до краху, тому що замість цього strcpy() одержує вказівник на ціле число. Подібні помилки досить просто не помітити, саме тому С++ забороняє неявне перетворення вказівника на voіd у вказівник на інший тип. Однак такий тип можна змінити явно, наприклад:
void main () {
// програма далі має помилку,
// але тепер вона компілюється!
// Перш за все необхідно перевірити
// явне перетворення типів...
pc = static_cast< char* >( pv );
char *pstr = new char[ strlen( pc )+1 ];
// скоріш за все призведе до конфлікту
strcpy( pstr, pc );
}
Іншою причиною використання явного перетворення типів може бути необхідність уникнути стандартного перетворення типів або виконати замість нього своє власне. Наприклад, в наступному виразі тип змінної ix спочатку перетворюється в тип double, потім до цієї змінної додається значення змінної dx, і далі результат знову трансформується в int:
int ix;
double dx;
ix += dx;
Але можна уникнути непотрібного перетворення, явно замінивши dx на int:
ix += static_cast< int >( dx );
Третьою причиною потреби явних перетворень є бажання уникнути неоднозначних ситуацій, в яких можливо декілька варіантів застосування правил перетворення за замовчуванням.
Синтаксис операцій явного перетворення типів має такий вигляд:
cast-name< type >( expression );
де cast-name – одне із ключових слів: static_cast, const_cast, dynamic_cast або reinterpret_cast, а type – тип, до якого приводиться вираз expression.
Чотири види явного перетворення були введені для того, щоб врахувати всі можливі форми приведення типів. Так const_cast необхідне для трансформації константного типу в неконстантний та рухомого (volatile) – в нерухомий. Наприклад:
extern char *string_copy( char* );
const char *pc_str;
char *pc = string_copy( const_cast< char* >( pc_str ));
Любе інше використання const_cast викличе помилку компіляції, як і спроба приведення за допомогою одного із трьох інших cast-операторів.
З використанням явного перетворенн static_cast можливі ті перетворення, які можуть бути виконані неявно на основі правил за замовчуванням:
double d = 67.0;
char ch = static_cast< char >( d );
Для чого використовується static_cast? Справа в тому, що без нього компілятор видасть попередження про можливу втрату точності. Застосування оператора statіc_cast говорить і компіляторові, і людині, що читає програму, що програміст знає про це.
Крім того, за допомогою static_cast вказівник void* можна перетворити на вказівник певного типу, арифметичне значення – в перелік (enum), а базовий клас – в похідний.
Ці зміни потенційно небезпечні, оскільки їх правильність залежить від того, яке конкретне значення приймає вираз в даний момент виконання програми:
enum en { first = 1, second, third };
extern int ix;
en m = static_cast< en >( ix );
Перетворення змінної ix в тип en буде правильним тільки в тому випадку, якщо змінна ix буде равною 1, 2 або 3.
Оператор reinterpret_cast працює з внутрішніми представленями об’єктів (re-interpret – інша інтерпритація того ж внутрішнього представлення), причому правильність даної операції цілком залежить від програміста, наприклад:
complex<double> *pcom;
char *pc = reinterpret_cast< char* >( pcom );
Програміст не повинен забути, який саме об’єкт реально адресується вказівником char *pc. Формально це вказівник на рядок вбудованого типу, і компілятор не буде забороняти використанню pc для ініціалізації рядка символів:
string str( pc );
хоча скоріш за все така команда викличе крах програми.
Це гарний приклад, що показує, наскільки небезпечні бувають явні перетворення типів. Програміст може присвоювати вказівникам одного типу значення вказівників зовсім іншого типу, і це буде працювати доти, поки програміст тримає ситуацію під контролем. Однак, забувши про деякі деталі перетворень, легко припуститися помилки, про яку компілятор не зможе попередити.
Особливо важко знайти подібну помилку, якщо явне перетворення типу робиться в одному файлі, а використовується змінене значення в іншому.
У деякому сенсі це відображає фундаментальний парадокс мови С++: строга перевірка типів покликана не допустити подібних помилок, у той же час наявність операторів явного перетворення дозволяє "обдурити" компілятор і використовувати об'єкти різних типів на свій страх і ризик.
В наведеному вище прикладі була “відключена” перевірку типів при ініціалізації вказівника pc і присвоєна йому адреса комплексного числа. При ініціалізації рядка str така перевірка виконується знову, проте компілятор вважає, що pc вказує на рядок, хоч, насправді, це не так!
Чотири оператори явного перетворення типів були введені в стандарт С++ як найменше зло при неможливості цілком заборонити таке приведення. Застаріла, але дотепер підтримувана стандартом С++ форма явного перетворення виглядає так:
char *pc = (char*) pcom;
Цей запис еквівалентний застосуванню оператора reіnterpret_cast, однак виглядає не так помітно. Використання cast-операторів дозволяє чітко вказати ті місця в програмі, де містяться потенційно небезпечні перетворення типів.
Якщо поводження програми стає помилковим або незрозумілим, можливо, у цьому винуваті явні перетворення типів вказівників. Використання операторів явного перетворення допомагає легко знайти місця в програмі, де такі операції виконуються, та проаналізувати їх.
Оператор dynamіc_cast застосовується для перетворення вказівника, що посилається на об'єкт типу класу у вказівник на тип класу з тієї ж ієрархії. Його також використовують для трансформації l-значення об'єкта типу класу в посилання на тип класу з тієї ж ієрархії. Приведення типів за допомогою оператора dynamіc_cast, на відміну від інших наявних у C++ способів, здійснюється під час виконання програми.
2.3. Застаріла форма явного перетворення
Оператори явного перетворення типів, представлені в попередньому розділі, з'явилися тільки в стандарті С++; раніш використовувалась форма, що тепер вважається застарілої. Хоча стандарт допускає і цю форму.
Застаріла форма явного перетворення має два види:
type (expr); // вид, що з’явився в С++
(type) expr; // вид, що існував в С
Застаріла форма може застусовуватись замість операторів static_cast, const_cast и reinterpret_cast.
От кілька прикладів такого використання:
const char *pc = (const char*) pcom;
int ix = (int) 6.17;
int ax = int( &ix );
Ця форма збережена в стандарті С++ тільки для забезпечення зворотної сумісності з програмами, написаними для С і попередніх версій С++.
3. ПОРЯДОК ВИКОНАННЯ РОБОТИ
1. При підготовці до лабораторної роботи, необхідно засвоїти теоретичний матеріал по темі і підготуватись до контрольного опитування по розумінню питань даної тематики.
2. Згідно з індивідуальним завданням розробити алгоритм розв’язання задачі.
3. Підготувати програмну реалізацію розробленого алгоритму. Засобами вбудованого тексто-вого редактора інтегрованого середовища набрати текст підготовленої програми. Відкомпілювати, налагодити та виконати програму.
4. Протестувати програму згідно зі складеною системою тестів і, при потребі, відкоректувати текст програми. Проаналізувати отримані результати.
5. Написати контрольне опитування по темі.
6. Оформити звіт по роботі.
Без підготовкі до лабораторної роботи (програмної реалізації розробленого алгоритму) студент до роботи не допускається.
4. ЗАВДАННЯ НА ЛАБОРАТОРНУ РОБОТУ
Завдання 1:
Визначити, які неявні перетворення типів будуть відбуватись при обчисленнях. З’ясувати, чи відбудуться втрати значимості даних. Хід міркувань підтвердити програмними результатами. В звіти пояснити кожне перетворення і метод, яким воно здійснено.
typedef unsigned int type_0;
typedef unsigned short int type_1;
typedef unsigned long int type_2;
typedef signed short int type_3;
typedef signed int type_4;
typedef signed long int type_5;
typedef bool type_6;
typedef char type_7;
typedef wchar_t type_8;
typedef float type_9;
typedef double type_10;
typedef long double type_11;
// позначимо через DN - день народження, MN – місяць народження
type_№ x0=0; // замість № підставити значення: DN % 9
type_№ x1=DN; // замість № підставити значення: DN % DN
type_№ x2=2; // замість № підставити значення: MN % 9
type_№ x3=3; // замість № підставити значення: MN % 13
type_№ x4=4; // замість № підставити значення: (DN * MN) % 9
type_№ x5=5; // замість № підставити значення: DN % 12
type_№ x6=6; // замість № підставити значення: MN % 3
type_№ x7=7; // замість № підставити значення: (DN * MN) % 12
type_№ x8=8; // замість № підставити значення: DN % 13
type_№ x9=9; // замість № підставити значення: MN % 6
type_№ x10=10; // замість № підставити значення: (DN * MN) % 10
type_№ x11=11; // замість № підставити значення: DN % 3
/* 1 */ x1 = x1 – 0xFFFFFFFA;
/* 2 */ x3 = ’a’ + x0 – x2;
/* 3 */ x7 = x4 + x5 + x6 * 0.1;
/* 4 */ x8 = x9 + x10 – x11*10;
Завдання 2
Визначити, які явні і неявні перетворення типів будуть відбуватись. Результати обчислень підтвердити програмними результатами. В звіти пояснити кожне перетворення і кожний отриманий результат.
// позначимо через DN і MN числа, що відповідають дню і місяцю народження,
// через dn і mn – по дві цифри, що відповідають дню і місяцю народження,
// наприклад: DN=5 , MN=3 , dn=05 , mn =03
const x_0=DN*MN*100; // наприклад: 5*3*100=1500 => x_0=1500;
const x_1= 0dnmn0; // наприклад: x_1=005030;
const x_2= 0xdnmn; // наприклад: x_2=0x0503;
const x_3= 0mndn0; // наприклад: x_3=003050;
const x_4= 0xmndn; // наприклад: x_4=0x0305;
const y_0=DN*MN/100; // наприклад: 5*3/100=0.15 => y_0=0.15;
const y_1= - DN.MN; // наприклад: y_1=-5.3;
const y_2= MN. DN e +2;