Наскільки важливим для вас є безпека виключень у вашому коді C ++?


19

Кожен раз, коли я вважаю зробити свій код сильним винятком безпечним, я виправдовую, що не роблю цього, оскільки це забирає багато часу. Розглянемо цей відносно простий фрагмент:

Level::Entity* entity = new Level::Entity();
entity->id = GetNextId();
entity->AddComponent(new Component::Position(x, y));
entity->AddComponent(new Component::Movement());
entity->AddComponent(new Component::Render());
allEntities.push_back(entity); // std::vector
entityById[entity->id] = entity; // std::map
return entity;

Для реалізації основної гарантії винятків я міг би використовувати покажчик масштабу для newдзвінків. Це дозволить уникнути витоку пам'яті, якщо будь-який з дзвінків повинен бути винятком.

Однак, скажімо, я хочу застосувати чітку гарантію виключення. Принаймні, мені потрібно було б реалізувати спільний покажчик для своїх контейнерів (я не використовую Boost), nothrow Entity::Swapдля додавання компонентів атомарним чином, і якусь ідіому для атомного додавання до Vectorі Map. Це не тільки забирає багато часу для впровадження, але й коштуватиме дорого, оскільки воно передбачає набагато більше копіювання, ніж небезпечне рішення виключення.

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

Наскільки далеко ви приймаєте це у власних ігрових проектах? Чи взагалі прийнятно писати небезпечний код для виключення для програми, яка може просто вийти з ладу, коли є виняток?


21
Коли я працював над проектами C ++ в минулому, то в основному ми робили вигляд, що винятків не існує.
Тетрад

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

7
Я особисто використовую винятки, щоб керувати .. hm .. 'виключеннями' не помилками. Мовляв, якщо я знаю, що функція може вийти з ладу, я дозволяю їй вийти з ладу, але якщо я знаю, що функція може не успішно читати архів, але є інший спосіб, я оточую її спробу, ловити. і якщо його виняток "не в змозі прочитати", я просто керую ним іншим способом, як я б обійшвся без читання архіву.
Густаво Масьєль

Що сказав Gtoknu, вам потрібно знати, які винятки можуть кинути будь-які зовнішні бібліотеки.
Пітер Ølsted

Відповіді:


26

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

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

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

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


12
Це одне коштує +1: "Код, який не використовує винятків і явно не є винятком, є безпечним, ніж код, який їх використовує, але наполовину визначає безпеку його виключень."
Максим Мінімус

14

Моє загальне правило: якщо вам потрібно використовувати винятки, використовуйте винятки. Якщо вам не потрібно використовувати винятки, не використовуйте винятки. Якщо ви не знаєте, чи потрібно використовувати винятки, вам не потрібно використовувати винятки.

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

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


5

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

Для розробки ігор я вважаю за краще спробувати знайти способи змусити свій код люб’язно продовжувати свої збої, коли це можливо.

Наприклад, я важко зашифрую спеціальну сітку ERROR у своїй грі. Це обертається червоне коло з білим X на ньому та білою облямівкою. Залежно від гри невидимий може бути кращим. У нього є один однотонний екземпляр, ініціалізований при запуску. Оскільки він введений у код, а не з використанням будь-яких зовнішніх файлів, його не може бути неможливим.

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

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

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

У випадку "нового", для розробки ігор, можливо, було б краще поглянути на використання якоїсь фабрики. Це може надати вам різні інші переваги, як попереднє розміщення об'єктів, перш ніж ви їх фактично потребуєте та / або робите їх масово. Це також полегшує створення таких речей, як глобальний індекс об'єктів, які ви можете використовувати для управління пам'яттю, знаходячи речі за ідентифікатором (хоча ви можете це робити і в конструкторах). І звичайно, ви також можете впоратися з помилками розподілу.


2

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

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

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

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

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

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


2

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

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


1

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

Цитуючи Джейсона Грегорі зі своєї книги « Game Engine Architecture» . (ВЕЛИКА КНИГА!)

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

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


2
Структурована обробка винятків - це певний тип обробки винятків, доступний у Windows, який, як правило, не такий, як C ++ винятки.
Килотан

До, я не можу повірити, що я цього не знав. Дякуємо за виправлення!
KlashnikovKid

0

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

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