Опасно ли ловить исключение по ссылке?


пожалуйста, взгляните на следующее исключение метания и ловли:

void some_function() {
    throw std::exception("some error message");
}

int main(int argc, char **argv) {
    try {
        some_function();
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        exit(1);
    }
    return 0;
}

безопасно ли поймать брошенное исключение по ссылке?

меня беспокоит то, что за исключением e на самом деле помещается в стек на some_function(). Но some_function() только что вернулся, причинив e подлежат уничтожению. Так что на самом деле сейчас e указывает на разрушенный объект.

правильно ли мое беспокойство?

каков правильный способ передать исключение, не копируя его по значению? Я должен бросить new std::exception() то есть он помещается в динамическую память?

4 63

4 ответа:

это действительно безопасно - и рекомендуется - поймать const ссылка.

"e фактически помещается в стек some_function()"

нет, это не так... фактически брошенный объект создается в неопределенной области памяти, зарезервированной для использования механизмом обработки исключений:

[за исключением.бросок] 15.1/4: память для объекта исключения выделяется неопределенным способом, за исключением случаев, указанных в пункте 3.7.4.1. за исключением объект уничтожается после того, как последний оставшийся активный обработчик для исключения завершает работу любым способом, отличным от повторного роста, или последний объект типа std::exception_ptr (18.8.5), который ссылается на объект исключения, уничтожается, в зависимости от того, что происходит позже.

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

15.1/5 когда брошенный объект является объектом класса, конструктор, выбранный для инициализации копирования и деструктора должен быть доступен, даже если операция копирования/перемещения исключена (12.8).


если это не нажал, он может помогите себе представить реализацию смутно так:

// implementation support variable...
thread__local alignas(alignof(std::max_align_t))
    char __exception_object[EXCEPTION_OBJECT_BUFFER_SIZE];

void some_function() {
    // throw std::exception("some error message");

    // IMPLEMENTATION PSEUDO-CODE:
    auto&& thrown = std::exception("some error message");
    // copy-initialise __exception_object...
    new (&__exception_object) decltype(thrown){ thrown };
    throw __type_of(thrown);
    // as stack unwinds, _type_of value in register or another
    // thread_local var...
}

int main(int argc, char **argv)
{
    try {
        some_function();
    } // IMPLEMENTATION:
      // if thrown __type_of for std::exception or derived...
      catch (const std::exception& e) {
        // IMPLEMENTATION:
        // e references *(std::exception*)(&__exception_object[0]);
        ...
    }
}

вы есть ловить по ссылке, в противном случае вы не сможете получить правильный динамический тип объекта. Что касается его срока службы, то стандарт гарантирует, в [except.throw],

объект исключения уничтожается после того, как последний оставшийся активный обработчик для исключения завершает работу любым способом, кроме перестроения, или уничтожается последний объект типа std:: exception_ptr (18.8.5), который ссылается на объект исключения, в зависимости от того, что позже

ловить по ссылке const это ровно как исключения должны быть пойманы. Объект исключения не обязательно живет "в стеке". Компилятор отвечает за соответствующую магию, чтобы сделать эту работу.

С другой стороны, ваш пример не может компилироваться с std::exception может быть сконструирован только по умолчанию или скопирован. В этом случае what() метод вернет указатель на пустую строку (C-style), которая не является особенно полезный.

предлагаю вам бросить std::runtime_error или std::logic_error соответственно, или класс, производный от него:

  • logic_error когда абонент запросил что-то вне проектных параметров вашего сервиса.
  • runtime_error когда абонент запросил что-то разумное, но внешние факторы мешают вам выполнить запрос.

http://en.cppreference.com/w/cpp/error/exception

С за исключением.брось:

выбрасывание исключения copy-инициализирует (8.5, 12.8) временный объект, вызывается объект исключения. Временные является lvalue и используется для инициализируйте переменную, объявленную в соответствующем обработчике (15.3). Если тип объекта исключения будет неполным типом или a указатель на неполный тип (возможно, резюме квалифицированных) Void программа плохо сформированные.

Это акт выбрасывание исключения, которое копирует объект исключения в области исключений, вне любого стека. Таким образом, совершенно законно и целесообразно ловить исключение по ссылке, так как время жизни объекта исключения будет продлеваться до последнего возможного catch().