А чи потрібні макроси

Макрос - самий неприємний інструмент С і C ++, перевертень, що ховається під личиною функції, кіт, який гуляє сам по собі і не звертає ніякої уваги на межі ваших областей видимості. Бережіться його!







Обговорення

Важко знайти мову, досить барвистий, щоб висловити все, що хочеться сказати про макроси. Але тим не менше наведемо кілька цитат.

Макроси з багатьох причин - вельми неприємна річ, яка може стати просто небезпечною. В першу чергу це пов'язано з тим, що макроси - засіб заміни тексту, чинне під час обробки вихідного тексту препроцесором, тобто ще до того, як почнеться якась перевірка синтаксису і семантики.

Мені не подобається більшість видів препроцесорів і макросів. Одна з цілей C ++ - зробити препроцесор C зайвим (§4.4, §18), оскільки я вважаю його великою помилкою.

Макроси майже ніколи не є необхідними в C ++. Використовуйте const (§5.4) або enum (§4.8) для визначення явних констант [см. рекомендацію 15], inline (§7.1.1) для того, щоб уникнути накладних витрат на виклик функції [але см. рекомендацію 8], template (глава 13) для визначення родин функцій і типів [см. рекомендації з 64 по 67], і namespace (§8.2) для того, щоб уникнути конфліктів імен

Перше правило щодо застосування макросів говорить: не використовуйте їх до тих пір, поки у вас не буде іншого виходу. Практично будь-який макрос свідчить про недосконалість мови програмування, програми або програміста.

Основна проблема з макросами C ++ полягає в тому, що вони виглядають набагато привабливіше, ніж є такими насправді. Макроси ігнорують області видимості, ігнорують інші можливості і правила мови, і замінюють всі символи, які скасовують за допомогою директиви #define. до самого кінця файлу. Застосування макросів зовні схожий на ім'я або виклик функції, але не має з ними нічого спільного. Макроси "негігієнічні", в тому сенсі, що вони можуть бути розкриті несподівано, причому перетворитися в залежності від контексту їх використання в самі різні конструкції. Підстановка тексту, виконувана макросами, робить написання хоча б незначною мірою "пристойного" макросу сумішшю мистецтва і чорної магії.

Програмісти, які вважають, що найважче розшифрувати помилки, пов'язані з шаблонами, ймовірно, просто ніколи не мали справи з погано написаними або невірно використаними макросами. Шаблони є частиною системи типів C ++, і тим самим дозволяють компілятору куди краще справлятися з ними, ніж з макросами, які мають мало спільного з мовою програмування. Гірше того, на відміну від шаблонів невірні макроси можуть бути розкриті в щось, що в силу чисто випадково скомпілюється, не маючи при цьому ніякого сенсу. І нарешті, помилка в макросі виявляється тільки після того, як макрос розкривається, а не при його визначенні.

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







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

макрос сприймає так, ніби йому передані два аргументи, а саме Foo. в той час як в дійсності ця конструкція являє собою єдиний об'єкт C ++.

винятки

Макроси залишаються єдино можливим рішенням для деяких важливих завдань, таких як захист директиви #include (див. Рекомендацію 24), використання директив #ifdef і #if defined для умовної компіляції та реалізація assert (див. Рекомендацію 68).

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

Можна (але обережно) використовувати макроси замість великої кількості копіювань і вставок близьких фрагментів коду.

17. Уникайте магічних чисел

Уникайте використання в коді літеральних констант на зразок 42 або 3.1415926. Такі константи не очевидні і ускладнюють супровід коду, оскільки вносять у нього трудноопределімую вид дублювання. Використовуйте замість них символьні імена і вислови на кшталт width * aspectRatiо.

Обговорення

Імена додають інформацію і вводять єдину точку супроводу; на відміну від них дубльовані по всій програмі звичайні числа анонімні і важко сопровождаемости. Константи повинні бути перерахуваннями або const -Значення, з відповідними областями видимості і іменами.

Одне число 42 може не бути тим же числом 42. що й інше. Що ще гірше, програміст може виконувати якісь обчислення "в умі" (наприклад: "Ось це 84 - просто подвоєне 42. яке було п'ятьма рядками раніше"), що зовсім заплутує код і робить подальшу заміну 42 інший константою джерелом величезної кількості помилок .

Приклад 1. Важливі константи з предметної області на рівні просторів імен.

Приклад 2. Константи, специфічні для даного класу. Ви можете визначити статичні інтегральні константи у визначенні класу; константи інших типів вимагають окремого визначення або застосування коротких функцій.

18. Оголошуйте змінні якомога локальнее

Уникайте "роздування" областей видимості. Змінних має бути якомога менше, а час їхнього життя - як можна коротше. Ця рекомендація по суті є окремим випадком рекомендації 10.

Обговорення

Змінні, час життя яких перевищує необхідне, мають ряд недоліків.

Вони роблять програму важко розуміється і супроводжуваної. Наприклад, чи повинен код оновлювати рядок path на рівні модуля, якщо змінений тільки поточний диск?

Вони засмічують контекст своїми іменами. Безпосереднім наслідком є ​​те, що змінні на рівні простору імен, найбільш видимі серед всіх інших, одночасно є і найгіршими (див. Рекомендацію 10).

Вони не завжди можуть бути коректно ініціалізовані. Ніколи не оголошуйте змінну до того, як ви зможете коректно її форматувати. Неініціалізовані змінні - джерело "розповзаються" помилок у всіх програмах С і C ++, і вимагають особливої ​​уваги у зв'язку з тим, що не завжди можуть бути виявлені компілятором (див. Рекомендацію 19).

Зокрема, старі версії мови С вимагали, щоб змінні були визначені тільки на початку області видимості; такий стиль в C ++ вийшов з ужитку. Серйозна проблема такого обмеження полягає в тому, що найчастіше на початку області видимості немає достатньої інформації для ініціалізації змінних. В результаті у вас залишається два виходи - або форматувати змінні деяким значенням за замовчуванням (наприклад, нулем), що зазвичай марнотратно і може привести до помилок (якщо змінна буде використана до того, як набуде деякий осмислений значення), або залишити їх неініціалізованих, що небезпечно. Неініціалізованих змінна призначеного для користувача типу буде самоініціалізіроваться деяким порожнім значенням.

Лікування цієї хвороби дуже просте - визначайте кожну змінну настільки локально, наскільки можете, що зазвичай означає точку безпосереднє перед її першим використанням, коли у вас вже достатньо даних для коректної ініціалізації.

винятки

Іноді з точки зору продуктивності може виявитися вигідним винесення змінної за межі циклу (див. Рекомендацію 9).

Оскільки константи не є частиною стану програми, дана рекомендація на них не поширюється (див. Рекомендацію 17).







Схожі статті