На цьому уроці ми розглянемо псевдоніми типів typedef і type alias в мові C++.
typedef
Ключове слово typedef дозволяє програмісту створити псевдонім для будь-якого типу даних і використовувати його замість фактичного імені типу. Для оголошення псевдоніму типу використовується ключове слово typedef
разом з типом даних, для якого створюється псевдонім, і далі вказується сам псевдонім. Наприклад:
1 2 3 4 5 |
typedef double time_t; // використовуємо time_t в якості псевдоніма для типу double // Наступні два стейтменти є еквівалентними double howMuch; time_t howMuch; |
Зазвичай до псевдонімів typedef додають закінчення _t
, вказуючи, таким чином, що ідентифікатором є тип, а не змінна.
typedef не визначає новий тип даних. Це просто псевдонім (інше ім’я) для вже існуючого типу. Його можна використовувати всюди, де використовується звичайний тип.
Навіть якщо наступне не має сенсу, воно все одно дозволено в мові C++:
1 2 3 4 5 6 7 8 |
typedef long miles_t; typedef long speed_t; miles_t distance = 8; speed_t phr = 2100; // Наступне дозволено, оскільки змінні distance і phr обидва є типу long distance = phr; |
typedef і читабельність коду
typedef використовується для покращення документації і розбірливості коду. Імена типів, такі як char, int, long, double і bool є чудовими для опису того, який тип повертає функція, але дуже часто нам хочеться знати, для якої саме мети повертається значення. Наприклад, розглянемо наступну функцію:
1 |
int GradeTest(); |
Ми бачимо, що значенням, що повертається, є ціле число, але що воно означає? Кількість пропущених питань? Ідентифікаційний номер учня? Код помилки? Сам int ні про що нам не говорить. Виправимо цю ситуацію:
1 2 |
typedef int testScore_t; testScore_t GradeTest(); |
З використанням типу testScore_t
, що повертається, стає очевидним, що функція повертає тип, значенням якого є результат тесту.
typedef і підтримка коду
typedef також дозволяє змінити базовий тип об’єкта без внесення змін в велику кількість коду. Наприклад, якщо ви використовували тип short для зберігання ідентифікаційного номера учня, але потім вирішили, що краще використовувати тип long, то вам доведеться переглянути величезну купу коду для заміни short на long. І, ймовірно, було б важко визначити, який з типів short використовується для зберігання ідентифікаційних номерів, а який — для інших цілей.
З typedef все, що вам потрібно зробити, — це змінити оголошення: typedef short studentID_t
на typedef long studentID_t
. Проте не варто забувати про обережність при зміні типу typedef на тип з іншого сімейства (наприклад, з int на float або навпаки)! Новий тип даних може мати проблеми з порівнянням або діленням цілих чисел/чисел типу з плаваючою крапкою, які старий тип не мав — про це слід пам’ятати.
typedef і кросплатформність
Ще однією великою перевагою typedef є можливість приховувати специфічні для певних платформ (операційних систем) деталі. На деяких платформах тип int займає 2 байти, на інших — 4 байти. Таким чином, використання типу int для зберігання більше 2 байтів інформації може бути потенційно небезпечним при написанні кросплатформного коду.
Оскільки char, short, int і long не вказують свій розмір, то для кросплатформних програм досить часто використовується typedef для визначення псевдонімів, які включають розмір типу даних в бітах. Наприклад, int8_t
— це 8-бітний signed int, int16_t
— це 16-бітний signed int, а int32_t
— це 32-бітний signed int.
typedef і спрощення складного
Хоча до сих пір ми розглядали тільки прості типи даних, в мові C++ ви можете побачити і наступні змінні/функції:
1 2 3 4 5 6 |
std::vector<std::pair<std::string, int> > pairlist; boolean hasAttribute(std::vector<std::pair<std::string, int> > pairlist) { // Щось робимо } |
Писати std::vector<std::pair<std::string, int> >
всякий раз, коли потрібно використовувати цей тип — не дуже ефективно і затратно як по часу, так і по докладеним зусиллям. Набагато простіше використати typedef:
1 2 3 4 5 6 7 8 |
typedef std::vector<std::pair<std::string, int> > pairlist_t; // використовуємо pairlist_t в якості псевдоніма для цього довжелезного типу даних pairlist_t pairlist; // оголошуємо pairlist_t boolean hasAttribute(pairlist_t pairlist) // використовуємо pairlist_t в якості параметра функції { // Щось робимо } |
Ось! Інша справа! Адже простіше використовувати pairlist_t
замість std::vector<std::pair<std::string, int> >
, чи не так?
Не переживайте, якщо ви ще не знаєте, що таке std::vector, std::pair та інше. Набагато важливіше зараз засвоїти, що за допомогою typedef ви можете давати прості імена складним типам даних, що зробить їх простішими як для використання, так і для розуміння.
type alias
У typedef також є свої нюанси. По-перше, легко забути, що пишеться першим: псевдонім типу чи ім’я типу. Наприклад:
1 2 |
typedef time_t double; // неправильно typedef double time_t; // правильно |
По-друге, синтаксис typedef стає менш привабливим в зв’язці зі складними типами даних (про це ми поговоримо детально, коли будемо розглядати вказівники на функції).
Для вирішення цих проблем в C++11 ввели новий покращений синтаксис для typedef, який імітує спосіб оголошення змінних. Цей синтаксис називається type alias. За допомогою type alias ми пишемо ім’я, яке потім використовується як синонім для конкретного типу даних (тобто принцип той же, але синтаксис зручніший).
Наступний typedef:
1 |
typedef double time_t; // використовуємо time_t в якості псевдоніма для типу double |
У С++11 можна оголосити як:
1 |
using time_t = double; // використовуємо time_t в якості псевдоніма для типу double |
Ці два способи функціонально еквівалентні.
Зверніть увагу, що хоч ми і використовуємо ключове слово using, воно не має нічого спільного з using-стейтментами. Це ключове слово має декілька значень в залежності від контексту.
Новий синтаксис створення псевдонімів є “чистішим” при використанні в складних ситуаціях, і його рекомендується використовувати замість звичайного typedef, якщо ваш компілятор підтримує C++11.
Правило: Використовуйте type alias замість typedef, якщо ваш компілятор підтримує C++11.
Тест
Завдання №1
Використовуючи наступний прототип функції:
1 |
int editData(); |
Конвертуйте тип значення int, що повертається, в status_t
, використовуючи ключове слово typedef. У відповіді до цього завдання вкажіть стейтмент typedef і оновлений прототип функції.
Відповідь №1
1 2 |
typedef int status_t; status_t editData(); |
Завдання №2
Використовуючи прототип функції з завдання №1, конвертуйте тип значення int, що повертається, в status_t
, використовуючи ключове слово using (C++11). У відповіді до цього завдання вкажіть стейтмент створення псевдоніма типу і оновлений прототип функції.
Відповідь №2
1 2 |
using status_t = int; status_t editData(); |