Урок №40. const, constexpr і символьні константи

  Юрій  | 

  Оновл. 28 Кві 2020  | 

 104

До цього моменту, всі змінні, які ми розглядали, були неконстантними. Їх значення можна було змінити в будь-який час, наприклад:

Проте, іноді корисно використовувати змінні, значення яких змінити не можна — константи.

Константи

Візьмемо, наприклад, величину сили тяжіння на Землі: 9.8м/с^2. Вона навряд чи зміниться найближчим часом. Використовувати константу в цьому випадку буде найкращим варіантом, тому що ми запобіжимо, таким чином, будь-яке (навіть випадкове) змінення цього значення.

Щоб зробити змінну константою, використовуйте ключове слово const перед типом змінної або після нього, наприклад:

Незважаючи на те, що C++ дозволяє розміщувати const як перед типом даних, так і після нього, хорошою практикою вважається розміщувати const перед типом даних.

Константи повинні бути ініціалізовані при оголошенні. Змінити їх значення за допомогою операції присвоювання не можна:

Оголошення константи без її ініціалізації також викличе помилку компіляції:

Зверніть увагу, константи можуть бути ініціалізовані з допомогою не константних значень:

const є найбільш корисним (і найчастіше використовується) з параметрами функцій:

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

Час компіляції і час виконання

Коли ви перебуваєте в процесі компіляції програми, то це є часом компіляції (англ. “compile time“). Компілятор перевіряє вашу програму на синтаксичні помилки і, якщо їх немає, конвертує код в об’єктні файли.

Коли ви перебуваєте в процесі запуску вашої програми або коли ваша програма вже виконується, то це є часом виконання (англ. “runtime“). Код виконується рядок за рядком.

Специфікатор constexpr

У C++ є два види констант:

   Константи часу виконання. Їх значення визначаються тільки під час виконання програми. Змінні типу usersAge і myValue вище є константами часу виконання, так як компілятор не може визначити їх значення під час компіляції. usersAge залежить від користувацького вводу (який можна отримати тільки під час виконання програми), а myValue залежить від значення, переданого в функцію (яке стане відомим також під час виконання програми).

   Константи часу компіляції. Їх значення визначаються під час компіляції програми. Наприклад, змінна зі значенням сили тяжіння на Землі є константою часу компіляції, так як ми її визначаємо під час написання програми (до початку її виконання).

У більшості випадків, не має значення який тип константи ви використовуєте: часу виконання або часу компіляції. Однак, все ж є декілька ситуацій, коли C++ може вимагати константу часу компіляції замість константи часу виконання (наприклад, при визначенні довжини масиву фіксованого розміру — ми розглянемо це трохи пізніше). Так як є 2 типи констант, то компілятору потрібно постійно відстежувати, до якого з них відноситься певна змінна. Щоб спростити це завдання, в C++11 додали специфікатор constexpr, який повідомляє компілятору, що поточна змінна є константою часу компіляції:

Використовувати його ви, швидше за все, не будете, але знати про нього не завадить.

Правило: Будь-яка змінна, яка не повинна змінювати своє значення після ініціалізації, повинна бути оголошена за допомогою специфікатора const (або constexpr).

Імена констант

Деякі програмісти пишуть імена констант великими літерами. Інші використовують звичайні імена, тільки з доданням префіксу 'k'. Ми ж не будемо їх якось виділяти, так як константи — це ті ж звичайні змінні, просто з фіксованими значеннями, ось і все. Особливої причини їх виділяти немає. Однак, це справа звички.

Символьні константи

У попередньому уроці ми говорили про магічні числа — літерали, які використовуються в програмі у вигляді констант. “Оскільки використовувати їх не рекомендується, то який вихід?” — запитаєте ви. А я відповім: “Використовувати символьні константи”. Символьна константа — це той же літерал (магічне число), тільки з ідентифікатором. Є два способи оголошення символьних констант в C++. Один з них хороший, а один — не дуже. Розглянемо обидва способи.

Поганий спосіб: Використовувати макроси-об’єкти з текстом_заміною в якості символьних констант.

Раніше цей спосіб широко використовувався, так що ви все ще можете це побачити в старих програмах.

Як ми вже знаємо, макроси-об’єкти мають дві форми: з текст_заміна і без текст_заміна. Розглянемо перший варіант: з текст_заміна. Він виглядає наступним чином:

#define ідентифікатор текст_заміна

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

Під час компіляції програми, препроцесор замінить всі ідентифікатори MAX_STUDENTS_PER_CLASS на літерал 30.

Погодьтеся, це набагато краще, ніж використовувати магічні числа, як мінімум, з кількох причин. MAX_STUDENTS_PER_CLASS забезпечує контекст того, що це за значення і навіщо воно використовується (що зрозуміло навіть без коментарів). По-друге, якщо число потрібно буде змінити — достатньо буде зробити правки тільки в директиві #define, всі інші ідентифікатори MAX_STUDENTS_PER_CLASS в програмі будуть автоматично замінені новим значенням при повторній компіляції.

Розглянемо ще один приклад:

Тут зрозуміло, що MAX_STUDENTS_PER_CLASS і MAX_NAME_LENGTH не є одним і тим же об’єктом, хоч і мають однакові значення.

Так чому ж цей спосіб поганий? На це є дві причини:

   По-перше, макроси опрацьовуються препроцесором, який замінює ідентифікатори на певні значення. Ці значення не відображаються в відлагоджувачеві (під час відлагоджування вашої програми). При компіляції int max_students = numClassrooms * 30; в відлагоджувачеві ви побачите int max_students = numClassrooms * MAX_STUDENTS_PER_CLASS;. “А як тоді дізнатися значення MAX_STUDENTS_PER_CLASS?” — запитаєте ви. А я відповім: “Вам доведеться самостійно знайти це в коді”. А процес пошуку може зайняти деякий час (в залежності від розмірів вашої програми).

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

Правило: Не використовуйте директиву #define для створення символьних констант.

Хороший спосіб: Використовувати змінні зі специфікатором const.

Наприклад:

Такі значення відображаються в відлагоджувачеві, а також дотримуються всіх правил звичайних змінних (в тому числі і тих, що стосуються області видимості).

Правило: Використовуйте специфікатор const для створення символьних констант.

Використання символьних констант в програмі

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

Алгоритм використання символьних констант у вашій програмі:

   Крок №1: Створіть заголовковий файл для зберігання констант.

   Крок №2: В заголовковому файлі оголосіть простір імен.

   Крок №3: Додайте всі ваші константи в створений вами простір імен (переконайтеся, що всі константи мають специфікатор const).

   Крок №4: #include заголовковий файл всюди, де потрібні константи.

Наприклад, файл constants.h:

Використовуйте оператор дозволу області видимості :: для доступу до констант в файлах .cpp:

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

Оцінити статтю:

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (Немає Оцінок)
Loading...

Залишити відповідь

Ваш E-mail не буде опублікований. Обов'язкові поля відмічені *