Nice programing

참조로 예외를 포착하는 것이 위험합니까?

nicepro 2020. 11. 23. 19:59
반응형

참조로 예외를 포착하는 것이 위험합니까?


다음 예외 throw 및 catch를 살펴보십시오.

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()동적 메모리에 배치되도록 던져야 합니까?


const참고 로 잡는 것이 실제로 안전하고 권장됩니다 .

" e실제로 스택에 배치됩니다 some_function()"

아닙니다. 실제로 던져진 객체는 예외 처리 메커니즘에서 사용하기 위해 예약 된 지정되지 않은 메모리 영역에 생성됩니다.

[except.throw] 15.1 / 4 : 3.7.4.1에 명시된 경우를 제외하고 예외 개체에 대한 메모리가 지정되지 않은 방식으로 할당됩니다. 예외 객체는 예외에 대해 마지막으로 남아있는 활성 처리기가 다시 발생하는 것 이외의 다른 방법으로 종료되거나 예외 객체를 참조하는 std :: exception_ptr (18.8.5) 유형의 마지막 객체가 삭제 된 후 삭제됩니다. .

지역 변수가에 지정 throw되면 필요한 경우 거기에 복사됩니다 (옵티마이 저가이 다른 메모리에서 직접 생성 할 수 있음). 그래서 ...

15.1 / 5 throw 된 객체가 클래스 객체 인 경우 복사 / 이동 작업이 제거 된 경우에도 복사 초기화를 위해 선택된 생성자와 소멸자에 액세스 할 수 있습니다 (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) 유형의 마지막 객체가 삭제 된 후 삭제됩니다.


Catching by const reference is exactly how exceptions should be caught. The exception object does not necessarily live 'on the stack'. The compiler is responsible for the appropriate magic to make this work.

On the other hand, your example cannot compile since std::exception may only be default-constructed or copy-constructed. In this case the what() method would return a pointer to an empty (c-style) string, which is not particularly useful.

Suggest you throw a std::runtime_error or std::logic_error as appropriate, or a class derived therefrom:

  • logic_error when the caller has requested something outside the design parameters of your service.
  • runtime_error when the caller has requested something reasonable but external factors prevent you from honouring the request.

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


From except.throw:

Throwing an exception copy-initializes (8.5, 12.8) a temporary object, called the exception object. The temporary is an lvalue and is used to initialize the variable declared in the matching handler (15.3). If the type of the exception object would be an incomplete type or a pointer to an incomplete type other than (possibly cv-qualified) void the program is ill-formed.

It's the act of throwing the exception that copies the exception object in the exceptions-area, outside of any stack. So it's perfectly legit, and advisable, to catch exception by reference, since the exception object lifetime will extend until the last possible catch().

참고URL : https://stackoverflow.com/questions/33387545/is-catching-an-exception-by-reference-dangerous

반응형