Для деяких мов (тобто C ++) витік ресурсів не повинен бути причиною
C ++ базується на RAII.
Якщо у вас є код, який міг би вийти з ладу, повернути або викинути (тобто, найбільш звичайний код), тоді вам повинен бути загорнутий вказівник всередині розумного вказівника (припускаючи, що у вас є дуже вагома причина, щоб ваш об'єкт не створювався в стеку).
Коди повернення більш багатослівні
Вони багатослівні і, як правило, перетворюються на щось на зразок:
if(doSomething())
{
if(doSomethingElse())
{
if(doSomethingElseAgain())
{
// etc.
}
else
{
// react to failure of doSomethingElseAgain
}
}
else
{
// react to failure of doSomethingElse
}
}
else
{
// react to failure of doSomething
}
Врешті-решт, ваш код - це сукупність ідентифікованих інструкцій (я бачив такий код у виробничому коді).
Цей код цілком можна перекласти такою мовою:
try
{
doSomething() ;
doSomethingElse() ;
doSomethingElseAgain() ;
}
catch(const SomethingException & e)
{
// react to failure of doSomething
}
catch(const SomethingElseException & e)
{
// react to failure of doSomethingElse
}
catch(const SomethingElseAgainException & e)
{
// react to failure of doSomethingElseAgain
}
Які чітко відокремлюють обробку коду та помилок, що може бути добре .
Коди повернення більш крихкі
Якщо не якесь неясне попередження від одного компілятора (див. Коментар "phjr"), їх можна легко проігнорувати.
З наведених вище прикладів припустимо, що хтось забув усунути можливу помилку (це трапляється ...). Помилка ігнорується, коли "повертається", і, можливо, вибухне пізніше (тобто покажчик NULL). Ця ж проблема не трапиться за винятком.
Помилка не буде проігнорована. Іноді ви хочете, щоб воно не вибухнуло, проте ... Тож ви повинні вибирати ретельно.
Коди повернення іноді потрібно перекладати
Скажімо, ми маємо такі функції:
- doSomething, що може повернути int з назвою NOT_FOUND_ERROR
- doSomethingElse, який може повернути значення bool "false" (для помилки)
- doSomethingElseAgain, який може повернути об'єкт Помилки (як із змінними __LINE__, __FILE__, так і з половиною стека.
- doTryToDoSomethingWithAllThisMess, що, ну ... Використовуйте наведені вище функції та повертайте код помилки типу ...
Який тип повернення doTryToDoSomethingWithAllThisMess, якщо одна з його викликаних функцій виходить з ладу?
Коди повернення не є універсальним рішенням
Оператори не можуть повернути код помилки. Конструктори С ++ теж не можуть.
Коди повернення означають, що ви не можете зв'язати вирази
Наслідок вищезазначеного пункту. Що робити, якщо я хочу написати:
CMyType o = add(a, multiply(b, c)) ;
Я не можу, тому що повернене значення вже використовується (а іноді його не можна змінити). Тож повернене значення стає першим параметром, що надсилається як посилання ... Або ні.
Винятки набираються
Ви можете надсилати різні класи для кожного виду виключення. Винятки з ресурсів (тобто нестача пам’яті) повинні бути легкими, але все інше може бути настільки важким, наскільки це потрібно (мені подобається виняток Java, який дає мені весь стек).
Кожен улов може бути спеціалізованим.
Ніколи не використовуйте улов (...) без перекидання
Зазвичай не слід приховувати помилку. Якщо ви не повторите викидання, принаймні, зафіксуйте помилку у файлі, відкрийте вікно повідомлення, що завгодно ...
Виняток становлять ... NUKE
Проблема за винятком полягає в тому, що надмірне їх використання створить код, повний спроб / ловів. Але проблема в іншому: хто намагається / зловити його / її код за допомогою контейнера STL? Проте ці контейнери можуть надсилати виняток.
Звичайно, в C ++ ніколи не дозволяйте винятку вийти з деструктора.
Виняток становлять ... синхронні
Обов’язково зловіть їх, перш ніж вони винесуть вашу нитку на коліна або розповсюдяться всередині циклу повідомлень Windows.
Рішенням може бути їх змішування?
Тож я думаю, що рішення - кинути, коли щось не повинно статися. А коли щось може статися, використовуйте код повернення або параметр, щоб користувач міг реагувати на це.
Отже, питання лише в тому, "що є того, що не повинно відбуватися?"
Це залежить від договору вашої функції. Якщо функція приймає вказівник, але вказує, що вказівник повинен бути не NULL, то нормально видавати виняток, коли користувач надсилає NULL-вказівник (питання в C ++, коли автор функції не використовував замість цього посилання покажчиків, але ...)
Іншим рішенням було б показати помилку
Іноді ваша проблема полягає в тому, що ви не хочете помилок. Використання винятків або кодів повернення помилок - це круто, але ... Ви хочете про це знати.
У моїй роботі ми використовуємо своєрідний «Асерт». Це, залежно від значень конфігураційного файлу, незалежно від параметрів налагодження / випуску компіляції:
- реєструвати помилку
- відкрити вікно повідомлення з повідомленням "Гей, у вас проблема"
- відкрити вікно повідомлень із написом "Гей, у вас проблема, чи не потрібно налагодити"
Як під час розробки, так і під час тестування це дозволяє користувачеві точно визначити проблему саме тоді, коли вона виявлена, а не пізніше (коли якийсь код дбає про повернене значення або всередині лову).
Його легко додати до застарілого коду. Наприклад:
void doSomething(CMyObject * p, int iRandomData)
{
// etc.
}
веде своєрідний код, подібний до:
void doSomething(CMyObject * p, int iRandomData)
{
if(iRandomData < 32)
{
MY_RAISE_ERROR("Hey, iRandomData " << iRandomData << " is lesser than 32. Aborting processing") ;
return ;
}
if(p == NULL)
{
MY_RAISE_ERROR("Hey, p is NULL !\niRandomData is equal to " << iRandomData << ". Will throw.") ;
throw std::some_exception() ;
}
if(! p.is Ok())
{
MY_RAISE_ERROR("Hey, p is NOT Ok!\np is equal to " << p->toString() << ". Will try to continue anyway") ;
}
// etc.
}
(У мене є подібні макроси, які активні лише під час налагодження).
Зверніть увагу, що у виробничій версії файл конфігурації не існує, тому клієнт ніколи не бачить результат цього макросу ... Але його легко активувати за потреби.
Висновок
Коли ви кодуєте за допомогою кодів повернення, ви готуєтесь до невдачі і сподіваєтесь, що ваша фортеця тестів достатньо безпечна.
Коли ви кодуєте, використовуючи виняток, ви знаєте, що ваш код може вийти з ладу, і, як правило, ставите контратаку на обрану стратегічну позицію у своєму коді. Але, як правило, ваш код більше стосується того, "що він повинен робити", а потім "того, що я боюся, що станеться".
Але коли ви кодуєте взагалі, ви повинні використовувати найкращий у вашому розпорядженні інструмент, а іноді це: "Ніколи не приховуйте помилку і показуйте її якомога швидше". Макрос, про який я говорив вище, дотримується цієї філософії.