C ++ 예외 처리 런타임은 어떻게 구현됩니까?


84

C ++ 예외 처리 메커니즘이 작동하는 방식에 흥미가 있습니다. 특히 예외 개체는 어디에 저장되며 포착 될 때까지 여러 범위를 통해 어떻게 전파됩니까? 일부 글로벌 영역에 저장됩니까?

이것이 컴파일러 특정 일 수 있기 때문에 누군가 g ++ 컴파일러 스위트의 맥락에서 이것을 설명 할 수 있습니까?


4
기사
읽으면

모르겠습니다.하지만 C ++ 사양에 명확한 정의가 있다고 생각합니다. (나는 틀릴 수도 있습니다)
Paul Nathan

2
아니요, 사양은 정의를 제공하지 않습니다. 구현이 아닌 동작을 지시합니다. 바울은, 당신은 당신이에 관심이있는 구현을 지정할 수 있습니다.
롭 케네디


답변:


49

구현은 다를 수 있지만 요구 사항에 따라 몇 가지 기본 아이디어가 있습니다.

예외 객체 자체는 하나의 함수에서 생성되고 호출자에서 파괴 된 객체입니다. 따라서 일반적으로 스택에 개체를 만드는 것은 불가능합니다. 반면에 많은 예외 객체는 그다지 크지 않습니다. 예를 들어 32 바이트 버퍼를 만들고 더 큰 예외 개체가 실제로 필요한 경우 힙으로 오버플로 할 수 있습니다.

실제 통제권 이전에는 두 가지 전략이 있습니다. 하나는 스택을 풀기에 충분한 정보를 스택 자체에 기록하는 것입니다. 이것은 기본적으로 실행할 소멸자 목록과 예외를 포착 할 수있는 예외 처리기입니다. 예외가 발생하면 일치하는 catch를 찾을 때까지 해당 소멸자를 실행하는 스택을 다시 실행하십시오.

두 번째 전략은이 정보를 스택 외부의 테이블로 이동합니다. 이제 예외가 발생하면 호출 스택을 사용하여 입력되었지만 종료되지 않은 범위를 찾습니다. 그런 다음 정적 테이블에서 조회하여 throw 된 예외가 처리 될 위치와 그 사이에서 실행되는 소멸자를 결정합니다. 이는 스택에 대한 예외 오버 헤드가 적다는 것을 의미합니다. 어쨌든 반환 주소가 필요합니다. 테이블은 추가 데이터이지만 컴파일러는 프로그램의 요구로드 세그먼트에 테이블을 넣을 수 있습니다.


4
AFAIR g ++는 아마도 C와의 호환성을 이유로 두 번째 주소 테이블 접근 방식을 사용합니다. Microsoft C ++ 컴파일러는 C ++ 예외가 SEH (구조적 예외 처리)를 기반으로 빌드되기 때문에 결합 된 접근 방식을 사용합니다. 각 C ++ 함수에서 MSC ++는이 특정 함수의 try-catch 블록 및 소멸자에 대한 주소 범위가있는 테이블을 가리키는 SEH 예외 처리 레코드를 만들고 등록합니다. 패키지에 C ++ 예외를 SEH 예외로 던지고 RaiseException ()을 호출하면 SEH가 제어를 C ++ 특정 핸들러 루틴에 반환합니다.
Anton Tykhyy

1
@Anton : 예, 주소 테이블 접근 방식을 사용합니다. 자세한 내용은 stackoverflow.com/questions/307610/… 에서 다른 질문에 대한 내 답변을 참조 하십시오.
CesarB

답변 해주셔서 감사합니다. C 순수 주의자들이 C ++와 그 예외를 어떻게 두려워하는지 알 수 있습니다. 간단한 try / catch가 런타임에 여러 스택 객체를 무의식적으로 생성하거나 추가 테이블로 프로그램을 부 풀릴 수 있다는 생각은 임베디드 시스템이 종종이를 피하는 이유입니다.
speedplane

@speedplane : 아니요, 이해 부족 때문입니다. 오류 처리는 결코 자유롭지 않습니다. C는 사용자가 직접 작성하도록 강요합니다. 그리고 우리 모두는 거의 사용되지 않는 코드 경로에서 a free()또는 하나 가 누락 된 C 프로그램의 수를 알고 fclose()있습니다.
MSalters

@MSalters 동의하지 않습니다. 거의 전적으로 이해가 부족합니다. 엔지니어는 종종 예외가 작동하는 방식과 예외가 코드에 어떤 영향을 미치는지 이해하지 못합니다. 따라서 예외를 사용할 때 주저하게됩니다. 예외 처리 구현이 더 명확하게 전달되고 마술처럼 보이지 않으면 많은 사람들이이를 사용하는 것을 주저하지 않을 것입니다.
스피드 플레인

20

이것은 15.1 표준 예외 던지기에 정의되어 있습니다.

던지기는 임시 개체를 만듭니다.
이 임시 개체에 대한 메모리가 할당되는 방법은 지정되지 않았습니다.

임시 개체 컨트롤을 만든 후 호출 스택에서 가장 가까운 처리기로 전달됩니다. 던지기와 잡기 지점 사이의 스택 풀기. 스택이 풀리면 스택 변수는 생성의 역순으로 파괴됩니다.

예외가 다시 발생하지 않는 한 임시는 포착 된 핸들러의 끝에서 파기됩니다.

참고 : 참조로 캐치하는 경우 참조는 임시를 참조하고 값으로 캐치하면 임시 객체가 값에 복사되므로 복사 생성자가 필요합니다.

S.Meyers의 조언 (const 참조로 잡기).

try
{
    // do stuff
}
catch(MyException const& x)
{
}
catch(std::exception const& x)
{
}

3
지정되지 않은입니다 뭔가 다른는 어떻게 프로그램이 스택을 풀어서 및 방법 은 "가장 가까운 핸들러는"여기서 프로그램이 알고있다. 나는 볼랜드가 그것을 구현하는 한 가지 방법에 대한 특허를 보유하고 있다고 확신합니다.
Rob Kennedy

객체가 생성의 역순으로 파괴되는 한 컴파일러 엔지니어가 아니라면 구현 세부 사항은 중요하지 않습니다.
Martin York

1
투표 : a) "S. Myers"가 아닌 "Scott Meyers"; b) 거짓 인용 : "Effective C ++": "항목 13 : 참조로 예외 포착 . ". 이를 통해 예외 개체에 정보를 조정 / 추가 할 수 있습니다.
Sebastian Mach 2011 년

3
@phresnel : 항목 21 : "가능하면 const를 사용하십시오"를 잊지 마십시오. 예외를 조정하는 좋은 사례는 없습니다. a) "고정 및 폐기", b) 다시 던지거나 c) 새로운 예외를 생성해야합니다.
Martin York

1
@phresnel : 네 이유가 있습니다 (논리에 동의하지 마십시오), 제 것이 있습니다.이 특정 주제 에 대해 그들과 이야기 했거나 실제로 그들의 마음을 안다고 주장하지는 않겠지 만 (Meyers, Alexandrescu 및 Sutter) 저는 믿습니다 나의 해석은 유효합니다. 그러나 시애틀 지역에있는 경우 North West C ++ 사용자 그룹 의 정규 참석자이므로 세 사람 모두와 대화 할 수 있습니다 (Meyers는 다른 사람보다 덜 자주).
Martin York

13

당신은 좀 걸릴 수 있습니다 여기에 에서 자세한 설명을 .

기본적인 예외 처리를 구현하기 위해 일반 C에서 사용되는 트릭을 살펴 보는 것도 도움이 될 수 있습니다. 이는 다음과 같은 방식으로 setjmp () 및 longjmp ()를 사용하는 것을 수반합니다. 전자는 예외 핸들러 (예 : "catch")를 표시하기 위해 스택을 저장하고 후자는 값을 "던지기"하는 데 사용됩니다. "thrown"값은 호출 된 함수에서 반환 된 것처럼 보입니다. "try 블록"은 setjmp ()가 다시 호출되거나 함수가 반환 될 때 종료됩니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.