C ++ 예외를 발생시키는 방법


259

예외 처리에 대한 이해가 매우 부족합니다 (예 : 내 목적에 맞게 throw, try, catch 문을 사용자 정의하는 방법).

예를 들어 다음과 같이 함수를 정의했습니다. int compare(int a, int b){...}

a 또는 b가 음수 일 때 일부 메시지에서 예외를 발생시키는 함수를 원합니다.

함수 정의에서 어떻게 접근해야합니까?


3
이을 읽어야 할 사람 : gotw.ca/publications/mill22.htm를 .
Oliver Charlesworth

37
@OliCharlesworth, 당신은 그것이 기본에 혼란스러워하는 누군가를 던질 것이라 생각하지 않습니까?
Mark Ransom

6
불필요한 예외는 피할 가치가 있습니다. 호출자가 음수 값을 전달하지 못하게 unsigned int하려면 함수 시그니처에서 매개 변수 로 지정하여 더 명확하게하십시오 . 그런 다음 다시 나는 학교에서 당신이 실제로 예외적 인 것들에 대해서만 예외를 던지고 잡아야한다고 생각합니다.
AJG85

1
@ Mark : 원래 throw()함수에서 예외 사양을 사용 해야하는지에 대한 질문을 오해했습니다 .
Oliver Charlesworth

답변:


363

단순한:

#include <stdexcept>

int compare( int a, int b ) {
    if ( a < 0 || b < 0 ) {
        throw std::invalid_argument( "received negative value" );
    }
}

표준 라이브러리에는 던질 수 있는 멋진 내장 예외 개체 모음이 제공됩니다 . 당신은 항상 가치에 의해 던져 참조로 잡아야한다는 것을 명심하십시오 :

try {
    compare( -1, 3 );
}
catch( const std::invalid_argument& e ) {
    // do stuff with exception... 
}

각 시도 후에 여러 catch () 문을 가질 수 있으므로 원하는 경우 다른 예외 유형을 개별적으로 처리 할 수 ​​있습니다.

예외를 다시 던질 수도 있습니다.

catch( const std::invalid_argument& e ) {
    // do something

    // let someone higher up the call stack handle it if they want
    throw;
}

그리고 유형에 관계없이 예외를 잡으려면 :

catch( ... ) { };

26
그리고 항상 const로 예외를 잡아야합니다
Adrian Cornish

2
사용자 정의 예외가 더 합리적이라면 @TerryLiYifeng를 사용하십시오. 여전히 std :: exception에서 파생하여 인터페이스를 동일하게 유지할 수 있습니다.
nsanders

2
다시 +1했지만 const가 매우 중요하다고 생각합니다. 그것이 임시 객체라는 사실을 강조하기 때문에 수정은 쓸모가 없습니다.
Adrian Cornish가

2
@AdrianCornish : 그것은 실제로 일시적인 것은 아닙니다. 일정하지 않은 캐치 가 유용 할 수 있습니다 .
GManNickG

26
일반적으로 throw;( throw e;포착 된 객체의 사본을 던져서 유형을 변경할 수 있음 ) 대신 단순 (원래 객체를 다시 던지고 유형을 유지)으로 다시 던질 것 입니다.
Mike Seymour

17

throw필요한 곳에 추가 try하고 오류를 처리하는 발신자를 차단하십시오. 관습에 따라에서 파생 된 것만 던져야 std::exception하므로 <stdexcept>먼저 포함하십시오 .

int compare(int a, int b) {
    if (a < 0 || b < 0) {
        throw std::invalid_argument("a or b negative");
    }
}

void foo() {
    try {
        compare(-1, 0);
    } catch (const std::invalid_argument& e) {
        // ...
    }
}

또한 Boost.Exception을 살펴 보십시오 .


15

이 질문은 다소 오래되었지만 이미 답변되었지만 C ++ 11에서 적절한 예외 처리를 수행하는 방법에 대한 메모를 추가하고 싶습니다.

사용 std::nested_exceptionstd::throw_with_nested

여기여기 에 스택 오버플로에 설명되어 있으며 중첩 된 예외를 다시 던질 적절한 예외 처리기를 작성하여 디버거 또는 번거로운 로깅없이 코드 내부의 예외에 대한 역 추적을 얻는 방법에 대해 설명합니다 .

파생 된 예외 클래스로이 작업을 수행 할 수 있으므로 이러한 역 추적에 많은 정보를 추가 할 수 있습니다! 역 추적이 다음과 같은 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"

8

특정 오류가 발생할 때 발생하는 메시지를 정의 할 수 있습니다.

throw std::invalid_argument( "received negative value" );

또는 다음과 같이 정의 할 수 있습니다.

std::runtime_error greatScott("Great Scott!");          
double getEnergySync(int year) {                        
    if (year == 1955 || year == 1885) throw greatScott; 
    return 1.21e9;                                      
}                                                       

일반적으로 다음 try ... catch과 같은 블록이 있습니다.

try {
// do something that causes an exception
}catch (std::exception& e){ std::cerr << "exception: " << e.what() << std::endl; }

6

사용자 정의 예외 의 경우 여기에 설명 된 다른 답변 에 추가 하고 싶었습니다 .

std::exception"모든 가능한"예외 유형을 발견 할 때로 부터 파생되는 사용자 정의 예외를 작성하는 경우, 항상 catch포착 될 수있는 "가장 파생 된"예외 유형으로 절을 시작해야합니다 . 하지 말아야 할 것의 예를보십시오 :

#include <iostream>
#include <string>

using namespace std;

class MyException : public exception
{
public:
    MyException(const string& msg) : m_msg(msg)
    {
        cout << "MyException::MyException - set m_msg to:" << m_msg << endl;
    }

   ~MyException()
   {
        cout << "MyException::~MyException" << endl;
   }

   virtual const char* what() const throw () 
   {
        cout << "MyException - what" << endl;
        return m_msg.c_str();
   }

   const string m_msg;
};

void throwDerivedException()
{
    cout << "throwDerivedException - thrown a derived exception" << endl;
    string execptionMessage("MyException thrown");
    throw (MyException(execptionMessage));
}

void illustrateDerivedExceptionCatch()
{
    cout << "illustrateDerivedExceptionsCatch - start" << endl;
    try 
    {
        throwDerivedException();
    }
    catch (const exception& e)
    {
        cout << "illustrateDerivedExceptionsCatch - caught an std::exception, e.what:" << e.what() << endl;
        // some additional code due to the fact that std::exception was thrown...
    }
    catch(const MyException& e)
    {
        cout << "illustrateDerivedExceptionsCatch - caught an MyException, e.what::" << e.what() << endl;
        // some additional code due to the fact that MyException was thrown...
    }

    cout << "illustrateDerivedExceptionsCatch - end" << endl;
}

int main(int argc, char** argv)
{
    cout << "main - start" << endl;
    illustrateDerivedExceptionCatch();
    cout << "main - end" << endl;
    return 0;
}

노트:

0)은 적절한 순서 ie- 먼저, 반대로되어야 catch (const MyException& e)하였다된다 catch (const std::exception& e).

당신이 볼 수 있듯이 같이 프로그램을 실행할 때 1), 첫 번째 캐치 절은 않았다 아마 무엇 인 (실행됩니다 NOT ) 처음에 싶었다.

2) 첫 번째 catch 절에서 잡은 유형이 유형이더라도 std::exception"적절한"버전 what()이 호출됩니다. 참조에 의해 잡히게됩니다 (적어도 잡힌 인수 std::exception유형을 값으로 변경 함) . "개체 슬라이싱"현상).

3) "XXX 예외가 발생했기 때문에 일부 코드 ..."가 예외 유형과 관련하여 중요한 작업을 수행하는 경우 여기에 코드의 오작동이 있습니다.

4) 이것은 잡힌 물체가 다음 class Base{};과 같은 "정상적인"물체 인 경우에도 관련이 있습니다 class Derived : public Base {}.

5) g++ 7.3.0Ubuntu 18.04.1에서 언급 된 문제를 나타내는 경고가 생성됩니다.

'void describeDerivedExceptionCatch ()'함수에서 : item12Linux.cpp : 48 : 2 : 경고 : 'MyException'유형의 예외 가 catch됩니다 (const MyException & e) ^ ~~~~

item12Linux.cpp : 43 : 2 : 경고 : 'std :: exception'catch (const exception & e) ^ ~~~~의 이전 핸들러에서

다시 말하지만 ,이 답변은 여기에 설명 된 다른 답변 에 추가 하는 것입니다 (이 시점은 언급 할 가치가 있다고 생각했지만 의견으로는 묘사 할 수 없습니다).

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