Студопедия

КАТЕГОРИИ:


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

Явне перетворення типів




Явне перетворення типів здійснюється за допомогою наступних 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++ способів, здійснюється під час виконання програми.




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


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


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



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




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