Когда полезна функция try block? - PullRequest
35 голосов
/ 10 апреля 2011

Мне интересно, когда программисты используют функциональные блоки try.Когда это полезно?

void f(int i)
try
{
   if ( i  < 0 ) 
      throw "less than zero";
   std::cout << "greater than zero" << std::endl;
}
catch(const char* e)
{
    std::cout << e << std::endl;
}

int main() {
        f(1);
        f(-1);
        return 0;
}

Вывод: (при ideone )

greater than zero
less than zero

РЕДАКТИРОВАТЬ: поскольку некоторые люди могут подумать, что синтаксис определения функции является неправильным(потому что синтаксис не выглядит знакомым), я должен сказать, что нет, это не неправильно.Это называется function-try-block.См. §8.4 / 1 [dcl.fct.def] в Стандарте C ++.

Ответы [ 6 ]

27 голосов
/ 10 апреля 2011

Вы используете его в конструкторах для отлова ошибок от инициализаторов. Обычно эти ошибки не обнаруживаются, так что это довольно исключительное использование.

В противном случае это бесполезно: если я не ошибаюсь,

void f() try { ... } catch (...) { ... }

строго эквивалентно

void f() { try { ... } catch (...) { ... } }
11 голосов
/ 10 апреля 2011

Функция try блока полезна для меня в двух контекстах.

a) Чтобы охватить все условия вокруг main(), позволяющие писать небольшие утилиты, не заботясь о локальной обработке ошибок:

int main()
try {
    // ...
    return 0;
}
catch (...) {
    // handle errors
    return -1;
}

, который явно является синтаксическим сахаром для попытки попробовать / поймать внутри main().

b) для обработки исключений, выданных конструкторами базового класса:

struct B {
     B() { /*might throw*/ }
};

struct A : B {
     A() 
     try : B() { 
         // ... 
     } 
     catch (...) {
         // handle exceptions thrown from inside A() or by B() 
     } 
};
8 голосов
/ 25 октября 2013

Помимо упомянутых функциональных применений, вы можете использовать функцию-try-block, чтобы сэкономить один уровень отступов. (Ack, ответ о стилях кодирования!)

Обычно вы видите примеры с функцией-try-block, например:

void f(/*...*/)
try {
   /*...*/
}
catch(/*...*/) {
    /*...*/
}

Где область действия функцииотступ в том же уровне, как если бы не было функции-try-block.Это может быть полезно, когда:

  • у вас есть ограничение на столбец в 80 символов, и вам придется переносить строки с учетом дополнительного отступа.
  • вы пытаетесь модифицировать существующую функцию с помощью try catchи не хочу трогать все строки функции.(Да, мы могли бы просто использовать git blame -w.)

Хотя, для функций, которые полностью заключены в блок-функции-try, я бы предложил не , чередующийся между некоторымифункции, использующие функцию-триггеры-блоки, а некоторые не в той же кодовой базе.Согласованность, вероятно, важнее проблем переноса строк.:)

6 голосов
/ 10 апреля 2011

Это может быть полезно, если вы хотите перехватывать исключения из инициализатора конструктора.

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

#include <iostream>

class A
{
public:
  A()
  try {
    throw 5;
  }
  catch (int) {
    std::cout << "exception thrown\n";
    //return; <- invalid
  }
};

int main()
{
  try {
    A a;
  }
  catch (...) {
    std::cout << "was rethrown";
  }
}
3 голосов
/ 24 февраля 2015

Замечания о том, как работают блоки попытки функции:

  • Для конструкторов блок попытки функции охватывает построение элементов данных и базовых классов.

  • Для деструкторов функциональный блок try включает уничтожение членов данных и базовых классов.Это становится сложным, но для C ++ 11 вы должны включить noexcept(false) в объявление вашего деструктора (или класса базового / членского класса), иначе любое исключение уничтожения приведет к завершению при завершении блока catch.Это можно предотвратить, поместив оператор return в блок catch (но это не сработает для конструкторов).

  • Блок catch в конструкторе или деструкторе долженбросить какое-то исключение (или оно неявно перезапустит пойманное исключение) .Недопустимо просто return (по крайней мере, в блоке захвата функции конструктора).Обратите внимание, однако, что вы можете вызвать exit() или аналогичный, что может иметь смысл в некоторых ситуациях.

  • Блок catch не может вернуть значение, поэтому он не работаетдля функций, возвращающих не void (если только они не намеренно завершают программу с exit() или подобным).По крайней мере, это , что я прочитал .

  • Блок catch для constructor-function-try не может ссылаться на элементы данных / базы, поскольку они будут иметьлибо 1) не были построены, либо 2) разрушены до вылова.Таким образом, блоки try функции бесполезны для очистки внутреннего состояния объекта - к тому времени, когда вы туда доберетесь, объект уже должен быть полностью «мертвым». Этот факт делает очень опасным использование функциональных блоков try в конструкторах, так как это правило трудно контролировать со временем, если ваши компиляторы не помечают его.

допустимое (легальное) использование

  • Перевод исключения (в другой тип / сообщение), сгенерированного во время конструктора или его конструкторов base / member.
  • Перевод илипоглощение и исключение, возникающее во время деструктора или его деструкторов по основанию / члену ( этикет деструктора несмотря на это).
  • Завершение программы (возможно, с полезным сообщением).
  • Какой-тосхемы регистрации исключений.
  • Синтаксический сахар для функций, возвращающих пустоты, которым требуется полностью инкапсулирующий блок try / catch.
2 голосов
/ 18 марта 2016

Еще одна вещь, для которой вы можете использовать их, - это предоставление дополнительных данных во время отладки таким образом, чтобы это не мешало завершенной сборке. Я не видел, чтобы кто-то еще использовал или защищал это, но это мне кажется удобным.

// Function signature helper.
#if defined(_WIN32) || defined(_WIN64)
    #define FUNC_SIG __FUNCSIG__
#elif defined(__unix__)
    #define FUNC_SIG __PRETTY_FUNCTION__
// Add other compiler equivalents here.
#endif  /* Function signature helper. */

void foo(/* whatever */)
#ifdef     DEBUG
try
#endif  /* DEBUG */
{
    // ...
}
#ifdef     DEBUG
catch(SomeExceptionOrOther& e) {
    std::cout << "Exception " << e.what() << std::endl
              << "* In function: " << FUNC_SIG << std::endl
              << "* With parameters: " << /* output parameters */ << std::endl
              << "* With internal variables: " << /* output vars */ << std::endl;

    throw;
}
#endif  /* DEBUG */

Это позволит вам одновременно получать полезную информацию при тестировании кода и легко выводить ее, не затрагивая ничего.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...