Подтвердить что ты не робот

Когда функция пытается использовать блок?

Мне интересно, когда программисты используют функциональные блоки 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;
}

Выход: (at ideone)

greater than zero
less than zero

EDIT: Как некоторые люди могут подумать, что синтаксис функции defintion неверен (поскольку синтаксис не выглядит привычным), я должен сказать, что нет его неправильного. Его называют функцией-try-block. См. §8.4/1 [dcl.fct.def] в стандарте С++.

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

a) Чтобы иметь предложение catch all 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() 
     } 
};

Ответ 3

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

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

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

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

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

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

Ответ 4

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

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

#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";
  }
}

Ответ 5

Заметки о том, как работают функции try block:

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

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

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

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

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

действительный (юридический) использует

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

Ответ 6

Нет, нет. В этом тривиальном случае это не полезно. Однако полезно инкапсулировать ctor-initialisers в try/catch (если ваш дизайн когда-либо достаточно сломан, чтобы потребовать его).

Ответ 7

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

// 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 */

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