std :: exception에서 상속해야합니까?


98

적어도 하나의 신뢰할 수있는 소스 (내가 취한 C ++ 클래스)에서 C ++의 애플리케이션 별 예외 클래스가 std::exception. 이 접근 방식의 이점에 대해 잘 모르겠습니다.

C #에서 상속하는 이유 ApplicationException는 분명합니다. 유용한 메서드, 속성 및 생성자 몇 가지를 얻고 필요한 항목을 추가하거나 재정의하기 만하면됩니다. 로 std::exception그것은 당신이 얻을 모두가 것 같다 what()당신은 단지뿐만 아니라 자신을 만들 수 있습니다 오버라이드 (override)하는 방법.

그렇다면 std::exception애플리케이션 별 예외 클래스에 대한 기본 클래스로 사용하면 어떤 이점이 있습니까? 상속하지 말아야 할 합당한 이유가 std::exception있습니까?


당신은 이것을보고 싶을 것입니다 : stackoverflow.com/questions/1605778/1605852#1605852
sbi

2
특정 질문과 관련이없는 부가 참고 사항이지만, C ++ 클래스는 반드시 그 자체로 좋은 사례에 대한 신뢰할 수있는 소스가 될 필요는 없습니다.
Christian Rau

답변:


69

주요 이점은 무엇을의 정확한 유형을 알고하지 않는 클래스를 사용하여 해당 코드 throw그것에을 할 수 있지만, 단지 . 편집 : Martin과 다른 사람들이 언급했듯이 실제로 헤더 에 선언 된 하위 클래스 중 하나에서 파생하려고합니다 .catchstd::exception

std::exception<stdexcept>


21
std :: exception에 메시지를 전달할 방법이 없습니다. std :: runtime_error는 문자열을 허용하며 std :: exception에서 파생됩니다.
Martin York

14
메시지를 예외 유형 생성자에 전달해서는 안됩니다 (메시지를 지역화해야 함을 고려하십시오.) 대신 오류를 의미 적으로 분류하는 예외 유형을 정의하고 사용자 친화적 인 메시지를 형식화하는 데 필요한 사항을 예외 객체에 저장합니다. , 그런 다음 캐치 사이트에서 수행하십시오.
에밀

std::exception되는 정의<exception>헤더 ( 증거 ). -1
Alexander Shukaev

5
그래서 당신의 요점은 무엇입니까? 정의는 선언을 의미합니다. 여기서 무엇이 잘못되었다고 보십니까?
Nikolai Fetissov

40

문제 std::exception는 메시지를 수락하는 생성자가 없다는 것입니다 (표준 호환 버전에서).

결과적으로 나는 std::runtime_error. 이것은 파생 std::exception되었지만 생성자를 사용하면 호출 될 때 std::string반환 될 생성자에 C-String 또는 a 를 전달할 수 있습니다 .char const*what()


11
던지는 시점에서 사용자 친화적 인 메시지를 포맷하는 것은 좋은 생각이 아닙니다. 이것은 낮은 수준의 코드와 지역화 기능 등을 결합하기 때문입니다. 대신 모든 관련 정보를 예외 개체에 저장하고 catch 사이트에서 예외 유형 및 전달되는 데이터를 기반으로 사용자 친화적 인 메시지 형식을 지정하도록합니다.
에밀

16
@ Emil : 예외 인 경우에는 물론 사용자가 표시 할 수있는 메시지를 전달하지만 일반적으로 로깅 목적으로 만 사용됩니다. throw 사이트에는 사용자 메시지를 작성할 컨텍스트가 없습니다 (어쨌든 라이브러리 일 수 있음). 캐치 사이트는 더 많은 컨텍스트를 가지며 적절한 메시지를 생성 할 수 있습니다.
Martin York

3
예외가 로깅 목적으로 만 사용된다는 생각을 어디서 얻었는지 모르겠습니다. :)
에밀

1
@Emil : 우리는 이미이 논쟁을 겪었습니다. 제가 지적한 부분은 당신이 예외를 두는 것이 아닙니다. 주제에 대한 이해가 좋아 보이지만 주장에 결함이 있습니다. 사용자에 대해 생성 된 오류 메시지는 개발자를위한 로그 메시지와 완전히 다릅니다.
Martin York

1
@Emil. 이 토론은 1 년 전입니다. 이 시점에서 자신의 질문을 만드십시오. 내가 우려하는 한 귀하의 주장은 잘못된 것입니다 (그리고 사람들이 나와 동의하는 투표에 근거). 내 라이브러리에 구현할 '적절한 수'의 예외는 무엇입니까?를 참조하십시오 . 그리고 나쁜 일이 정말 일반적인 예외를 잡기인가? 이전 diatribe 이후 nw 정보를 제공하지 않았습니다.
Martin York

17

에서 상속하는 이유는 std::exception예외에 대한 "표준"기본 클래스이므로 팀의 다른 사람들이이를 예상하고 base를 잡는 것은 자연스러운 일입니다 std::exception.

편의를 찾고 있다면 생성자 std::runtime_error를 제공하는 상속을받을 수 있습니다 std::string.


2
std :: exception을 제외한 다른 표준 예외 유형에서 파생하는 것은 아마도 좋은 생각이 아닙니다. 문제는 std :: exception에서 비 가상적으로 파생되므로 catch (std :: exception &)가 std :: exception으로의 변환으로 인해 예외를 자동으로 포착하지 못할 수있는 다중 상속과 관련된 미묘한 버그로 이어질 수 있습니다. 모호한.
Emil 2011 년

2
주제에 대한 부스트에 대한 기사를 읽었습니다. 순수한 논리적 인 의미에서 합리적으로 보입니다. 그러나 나는 야생의 예외에서 MH를 본 적이 없습니다. 또한 예외에서 MH를 사용하는 것을 고려하지 않을 것이므로 존재하지 않는 문제를 해결하는 것은 큰 망치처럼 보입니다. 이것이 진짜 문제 였다면 그러한 명백한 결함을 수정하기 위해 표준위원회에서 조치를 취했을 것이라고 확신합니다. 그래서 내 의견은 std :: runtime_error (및 패밀리)가 여전히 던지거나 파생되는 예외로 완벽하게 허용된다는 것입니다.
Martin York

5
@Emil : 다른 표준 예외에서 파생 std::exception하는 것은 훌륭한 아이디어입니다. 하지 말아야 할 것은 이상의 상속을받는 것입니다.
Mooing Duck 2013 년

@MooingDuck : 둘 이상의 표준 예외 유형에서 파생 된 경우 std :: exception이 (가상이 아닌) 여러 번 파생됩니다. boost.org/doc/libs/release/libs/exception/doc/…을 참조하십시오 .
에밀

1
@Emil : 그것은 내가 말한 것을 따른다.
Mooing Duck 2013 년

13

나는 이전의 저자들이 int, HRESULTS, std :: string, char *, random classes ... 모든 곳에서 다른 것들을 던진 대규모 코드베이스 정리에 참여한 적이 있습니다. 유형의 이름을 지정하면 아마도 어딘가에 던져졌을 것입니다. 그리고 공통 기본 클래스가 전혀 없습니다. 저를 믿으십시오. 던져진 모든 유형이 우리가 잡을 수있는 공통 기반을 가지고 있고 아무것도 지나칠 수 없다는 것을 알면 상황이 훨씬 더 깔끔해졌습니다. 그러니 여러분 자신 (그리고 앞으로 여러분의 코드를 유지해야 할 사람들)에게 호의를 베푸시고 처음부터 그렇게하세요.


12

boost :: exception 에서 상속해야합니다 . 추가 데이터를 전달하는 더 많은 기능과 잘 알려진 방법을 제공합니다. 물론 Boost를 사용하지 않는 경우이 제안을 무시하십시오.


5
그러나 boost :: exception 자체는 std :: exception에서 파생되지 않습니다. boost :: exception에서 파생 된 경우에도 std :: exception에서도 파생되어야합니다 (별도의 참고로 예외 유형에서 파생 될 때마다 가상 상속을 사용해야합니다.)
Emil

10

예,에서 파생해야합니다 std::exception.

다른 사람들은 std::exception문자 메시지를 전달할 수 없다는 문제 가 있다고 대답 했지만 일반적으로 던지는 시점에서 사용자 메시지를 형식화하는 것은 좋지 않습니다. 대신 예외 개체를 사용하여 모든 관련 정보를 catch 사이트로 전송 한 다음 사용자 친화적 인 메시지 형식을 지정할 수 있습니다.


6
+1, 좋은 지적입니다. 관심사의 분리와 i18n 관점 모두에서 프레젠테이션 계층이 사용자 메시지를 구성하도록하는 것이 확실히 낫습니다.
John M Gant

@JohnMGant와 Emil : 흥미롭게도, 이것이 어떻게 수행 될 수 있는지에 대한 구체적인 예를 가리킬 수 있습니까? 나는 std::exception예외의 정보 에서 파생 되고 전달할 수 있음을 이해합니다 . 그러나 누가 오류 메시지를 작성 / 형식화 할 책임이 있습니까? ::what()기능 또는 뭔가 다른?
alfC 2014-06-25

1
캐치 사이트 (라이브러리가 아닌) 수준에서 메시지 서식을 지정합니다. 유형별로 파악한 다음 정보를 조사한 다음 적절한 사용자 메시지를 지역화 / 형식화합니다.
에밀

9

상속을 원하는 이유 std::exception는 그 클래스에 따라 잡힌 예외를 던질 수 있기 때문입니다.

class myException : public std::exception { ... };
try {
    ...
    throw myException();
}
catch (std::exception &theException) {
    ...
}

6

상속에 대해 알아야 할 한 가지 문제는 객체 슬라이싱입니다. throw e;throw-expression 을 작성할 때 예외 객체라고하는 임시 객체를 초기화합니다.이 객체의 유형은 피연산자의 정적 유형에서 최상위 수준 cv 한정자를 제거하여 결정됩니다 throw. 그것은 당신이 기대하는 것이 아닐 수 있습니다. 여기에서 찾을 수있는 문제의 예 .

상속에 대한 주장이 아니라 '필수'정보 일뿐입니다.


3
제 생각에 "e를 던져라"는 것입니다. 악하고 "던지기" 괜찮습니다.
James Schek 2009

2
예, throw;괜찮습니다.하지만 이런 식으로 작성해야하는 것은 분명하지 않습니다.
Kirill V. Lyadvinsky 2009

1
"throw e"를 사용하여 rethrow가 수행되는 Java 개발자에게는 특히 고통 스럽습니다.
James Schek 2009

추상 기본 유형에서만 파생하는 것을 고려하십시오. 추상 기본 유형을 값으로 잡으면 오류가 발생합니다 (슬라이싱 방지). 참조로 추상 기본 유형을 잡은 다음 그 사본을 던지려고하면 오류가 발생합니다 (다시 슬라이싱을 피합니다.)
Emil

다음 raise()과 같은 멤버 함수를 추가하여이 문제를 해결할 수도 있습니다 virtual void raise() { throw *this; }. 파생 된 각 예외에서 재정의하는 것을 잊지 마십시오.
저스틴 시간 - 분석 재개 모니카

5

차이점 : std :: runtime_error 대 std :: exception ()

상속 여부는 귀하 에게 달려 있습니다. 표준 std::exception및 그 하위 규격은 하나 명의 가능한 예외 계층 구조 (분할에 제안 logic_errorsubhierarchy 및 runtime_errorsubhierarchy) 및 하나의 가능한 예외 객체 인터페이스. 당신이 그것을 좋아한다면-그것을 사용하십시오. 어떤 이유로 다른 것이 필요한 경우 고유 한 예외 프레임 워크를 정의하십시오.


3

언어가 이미 std :: exception을 던 졌기 때문에 괜찮은 오류보고를 제공하기 위해 어쨌든 그것을 잡아야합니다. 예상치 못한 모든 예외에 대해 동일한 캐치를 사용할 수도 있습니다. 또한 예외를 발생시키는 거의 모든 라이브러리는 std :: exception에서 파생됩니다.

즉,

catch (...) {cout << "Unknown exception"; }

또는

catch (const std::exception &e) { cout << "unexpected exception " << e.what();}

그리고 두 번째 옵션은 확실히 더 좋습니다.


3

가능한 모든 예외가에서 파생되는 std::exception경우 catch 블록은 catch(std::exception & e)모든 것을 간단하게 캡처 할 수 있습니다 .

예외를 캡처 한 후에는 해당 what메서드를 사용 하여 자세한 정보를 얻을 수 있습니다. C ++는 덕 타이핑을 지원하지 않으므로 what메서드가 있는 다른 클래스에서이를 사용하려면 다른 catch와 다른 코드가 필요합니다.


7
std :: exception을 하위 클래스로 분류하지 말고 (std :: exception e)를 잡으십시오. std :: exception & (또는 포인터)에 대한 참조를 잡아야합니다. 그렇지 않으면 하위 클래스 데이터를 잘라 내야합니다.
jmucchiello

1
이제 그것은 멍청한 실수였습니다. 확실히 더 잘 압니다. stackoverflow.com/questions/1095225/…
Mark Ransom

3

표준 예외 유형에서 파생할지 여부가 첫 번째 질문입니다. 이렇게하면 모든 표준 라이브러리 예외 및 사용자 고유에 대해 단일 예외 처리기를 사용할 수 있지만 이러한 모든 예외 처리기를 권장합니다. 문제는 처리 방법을 알고있는 예외 만 포착해야한다는 것입니다. 예를 들어 main ()에서 모든 std :: exceptions를 잡는 것은 종료하기 전에 what () 문자열이 최후의 수단으로 기록 될 경우 좋은 일입니다. 그러나 다른 곳에서는 좋은 생각이 아닐 것입니다.

표준 예외 유형에서 파생할지 여부를 결정한 후에는 어느 것이 기본이되어야하는지에 대한 질문이 있습니다. 응용 프로그램에 i18n이 필요하지 않은 경우 호출 사이트에서 메시지 서식을 지정하는 것이 정보를 저장하고 호출 사이트에서 메시지를 생성하는 것만 큼 좋다고 생각할 수 있습니다. 문제는 형식화 된 메시지가 필요하지 않을 수 있다는 것입니다. 지연 메시지 생성 체계를 사용하는 것이 더 좋습니다. 아마도 미리 할당 된 메모리를 사용하는 것입니다. 그런 다음 메시지가 필요한 경우 액세스시 생성되고 예외 개체에 캐시 될 수 있습니다. 따라서 메시지가 throw 될 때 생성되면 std :: runtime_error와 같은 std :: exception 파생이 기본 클래스로 필요합니다. 메시지가 느리게 생성되는 경우 std :: exception이 적절한 기준입니다.


0

하위 클래스 예외가 발생하는 또 다른 이유는 캡슐화 된 대규모 시스템에서 작업 할 때 더 나은 설계 측면입니다. 유효성 검사 메시지, 사용자 쿼리, 치명적인 컨트롤러 오류 등과 같은 것에 재사용 할 수 있습니다. 메시지와 같은 모든 유효성 검사를 다시 작성하거나 다시 연결하는 대신 기본 소스 파일에서 간단히 "catch"할 수 있지만 전체 클래스 집합에서 오류가 발생합니다.

예를 들어 치명적인 예외는 프로그램을 종료하고 유효성 검사 오류는 스택을 지우고 사용자 쿼리는 최종 사용자에게 질문을합니다.

이렇게하면 동일한 클래스를 다른 인터페이스에서 재사용 할 수도 있습니다. 예를 들어 Windows 응용 프로그램은 메시지 상자를 사용할 수 있으며 웹 서비스는 html을 표시하고보고 시스템은이를 기록하는 식입니다.


0

이 질문은 다소 오래되었고 이미 많은 답변을 받았지만 예외에 대한 토론에서 계속해서 이것을 놓치고 있기 때문에 C ++ 11에서 적절한 예외 처리를 수행하는 방법에 대한 메모를 추가하고 싶습니다.

사용 std::nested_exceptionstd::throw_with_nested

StackOverflow herehere 에 설명 되어 있으며, 중첩 된 예외를 다시 발생시키는 적절한 예외 처리기를 작성하여 디버거 나 번거로운 로깅없이 코드 내부의 예외에 대한 역 추적을 얻는 방법에 대해 설명합니다 .

파생 된 예외 클래스를 사용하여이를 수행 할 수 있으므로 이러한 역 추적에 많은 정보를 추가 할 수 있습니다! GitHub 에서 내 MWE를 살펴볼 수도 있습니다. 역 추적은 다음과 같습니다.

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

std::runtime_error예외가 발생할 때 많은 정보를 얻기 위해 하위 클래스 를 만들 필요조차 없습니다 .

서브 클래 싱에서 볼 수있는 유일한 이점 std::runtime_error은 예외 처리기가 사용자 정의 예외를 포착하고 특별한 작업을 수행 할 수 있다는 것입니다. 예를 들면 :

try
{
  // something that may throw
}
catch( const MyException & ex )
{
  // do something specialized with the
  // additional info inside MyException
}
catch( const std::exception & ex )
{
  std::cerr << ex.what() << std::endl;
}
catch( ... )
{
  std::cerr << "unknown exception!" << std::endl;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.