Якщо випустити «деструкторів» в C, це занадто далеко - це YAGNI?


9

Я працюю над середнім вбудованим додатком в C, використовуючи ОО-подібні методи. Мої "класи" - це .h / .c модулі, що використовують структури даних та функціональні покажчики для імітації інкапсуляції, поліморфізму та введення залежності.

Тепер можна було б очікувати, що myModule_create(void)функція прийде разом із myModule_destroy(pointer)колегою. Але проект, який впроваджується, ніколи не повинен бути вивільнений, реалістично створені ресурси.

Я маю на увазі, якщо у мене є 4 послідовних порти UART і я створюю 4 екземпляри UART з необхідними штифтами та налаштуваннями, немає абсолютно жодної причини ніколи не хотіти знищувати UART №2 в якийсь момент під час виконання.

Отже, слідуючи принципу YAGNI (вам це не знадобиться), я повинен опускати деструкторів? Це здається мені надзвичайно дивним, але я не можу придумати їх користь; ресурси звільняються, коли пристрій вимикається.


4
Якщо ви не надаєте спосіб розпоряджатися об'єктом, ви передаєте чітке повідомлення про те, що колись створений "нескінченний" час життя. Якщо це має сенс для вашої заявки, я кажу: зробіть це.
гламперт

4
Якщо ви збираєтеся зайти так далеко, з'єднавши тип із вашим конкретним випадком використання, навіщо навіть myModule_create(void)функціонувати? Ви можете просто жорстко кодувати конкретні екземпляри, які ви очікуєте використовувати в інтерфейсі, який ви відкриєте.
Doval

@Doval Я подумав про це. Я стажер, використовуючи частини та біти коду від мого керівника, тому я намагаюся жонглювати «робити це правильно», експериментуючи зі стилем OO на C для досвіду та відповідністю стандартам компанії.
Asics

2
@glampert цвяхи його; Додам, що ви повинні очистити очікуваний нескінченний термін експлуатації в документації функції створення.
Blrfl

Відповіді:


11

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

Гламперт правий; тут немає потреби в деструкторах. Вони просто створюватимуть розрив коду та провалля для користувачів (використання об'єкта після його деструктора називається невизначеним поведінкою).

Однак ви повинні бути впевнені, що утилізувати предмети дійсно не потрібно. Наприклад, чи потрібно мати об’єкт для UART, який наразі не використовується?


3

Найпростіший спосіб виявити витоки пам’яті - це можливість чисто вийти з програми. Багато компіляторів / середовищ надають спосіб перевірити наявність пам’яті, яка все ще виділяється при виході програми. Якщо його не надано, зазвичай існує спосіб додати код безпосередньо перед виходом, який може розібратися.

Отже, я б, безумовно, пропонував конструкторам, деструкторам та логіці відключення навіть у вбудованій системі, яка "теоретично" ніколи не повинна виходити для полегшення виявлення витоку пам'яті. Насправді виявлення витоку пам'яті ще важливіше, якщо код програми ніколи не повинен виходити.


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

@Codes: Немає підстав обмежувати себе. Хоча ви можете придумати чудовий дизайн, який попередньо виділяє пам'ять під час запуску, коли люди, які ви приїжджаєте, не прихильні до цієї грандіозної схеми або не бачать її важливості, вони будуть виділяти пам'ять на літати і там іде ваш дизайн. Просто зробіть це правильно та виділіть / виділіть та перевірте, що те, що ви реалізували, насправді працює. Якщо у вас справді є пристрій з обмеженою пам’яттю, то зазвичай робиться це перекриття нового оператора / malloc та збереження блоків розподілу.
Данк

3

У моїй розробці, яка широко використовує непрозорі типи даних для сприяння підходу, що нагадує ОО, я надто боровся з цим питанням. Спочатку я був рішуче в таборі усунення деструктора з точки зору YAGNI, а також з точки зору MISRA "мертвого коду". (У мене було багато ресурсного приміщення, це не було врахуванням.)

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

Тому, з мого досвіду, "aint't" в YAGNI виявляється "є", і я в кінцевому підсумку створив деструктори для кожного класу, я вважав, мені це потрібно чи ні. Навіть якщо я пропустив тестування, у принаймні правильно розробленого деструктора для бідних людей, які слідкують за мною, буде такий. (І, що значно менше значення, він робить код більш багаторазовим, оскільки його можна використовувати в середовищі, де він буде знищений.)

Хоча це адресує YAGNI, він не адресує мертвий код. Для цього я вважаю, що умовний компілюючий макрос на кшталт #define BUILD_FOR_TESTING дозволяє усунути деструктор з остаточної збірки виробництва.

Це робиться таким чином: у вас є деструктор для тестування / подальшого повторного використання, і ви задовольняєте проектні цілі YAGNI та правила "без мертвого коду".


Будьте уважні щодо # ifdef'ing свого тесту / коду prod. Це досить безпечно при застосуванні до цілої функції, як ви описуєте, адже якщо функція насправді потрібна, компіляція не вдасться. Однак використання вбудованої функції #ifdef всередині функції набагато ризикованіше, оскільки ви зараз тестуєте інший кодовий шлях, ніж те, що працює у програмі.
Кевін

0

У вас може бути деструктор без операцій, щось подібне

  void noop_destructor(void*) {};

потім встановіть деструктор, Uartможливо, використовуючи

  #define Uart_destructor noop_destructor

(додати відповідний склад, якщо він потрібен)

Не забудьте документувати. Можливо, ви хочете навіть

 #define Uart_destructor abort

Альтернативно, окремий випадок у загальному коді виклику деструктора - випадок, коли функція вказівника деструктора NULLуникає його виклику.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.