C++에서 예외 및 스택 해제

C++ 예외 메커니즘에서 컨트롤은 throw 문에서 throw된 형식을 처리할 수 있는 첫 번째 catch 문으로 이동합니다. catch 문에 도달하면 throw 문과 catch 문 사이의 범위에 있는 모든 자동 변수가 스택 해제라고 하는 프로세스에서 제거됩니다. 스택 해제에서 실행은 다음과 같이 진행됩니다.

  1. 컨트롤은 try 일반 순차적 실행을 통해 문에 도달합니다. 블록의 try 보호된 섹션이 실행됩니다.

  2. 보호된 섹션 catch 을 실행하는 동안 예외가 throw되지 않으면 블록 뒤에 try 있는 절이 실행되지 않습니다. 연결된 try 블록 뒤에 있는 마지막 catch 절 뒤에 있는 문에서 실행이 계속됩니다.

  3. 보호된 섹션을 실행하는 동안 또는 보호된 섹션이 직접 또는 간접적으로 호출하는 루틴에서 예외가 throw되면 피연산자가 만든 개체에서 예외 개체가 만들어 throw 집니다. (이는 복사 생성자가 관련될 수 있음을 의미합니다.) 이 시점에서 컴파일러는 throw되는 형식의 예외를 처리할 수 있는 더 높은 실행 컨텍스트에서 또는 모든 유형의 예외를 catch 처리할 수 있는 처리기를 찾 catch 습니다. catch 처리기는 블록 뒤 try 의 모양 순서로 검사됩니다. 적절한 처리기를 찾을 수 없으면 다음 동적으로 바깥쪽 try 블록이 검사됩니다. 이 프로세스는 가장 바깥쪽 바깥쪽 try 블록을 검사할 때까지 계속됩니다.

  4. 일치하는 처리기를 여전히 찾을 수 없거나 해제 프로세스 중 처리기가 컨트롤을 갖기 전에 예외가 발생하는 경우 미리 정의된 런타임 함수 terminate가 호출됩니다. 예외가 throw되었지만 해제 작업을 시작하기 전에 예외가 발생하는 경우 terminate가 호출됩니다.

  5. 일치하는 catch 처리기가 발견되고 값으로 catch되는 경우 예외 개체를 복사하여 공식 매개 변수가 초기화됩니다. 참조로 catch하면 예외 개체를 참조하도록 매개 변수가 초기화됩니다. 정식 매개 변수가 초기화된 후 스택 해제 프로세스가 시작됩니다. 여기에는 처리기와 연결된 블록의 시작 try 부분과 예외의 throw 사이트 사이에 완전히 생성되었지만 아직 소멸되지 않은 모든 자동 개체가 소멸됩니다 catch . 소멸은 생성과 반대 순서로 발생합니다. catch 처리기가 실행되고 프로그램은 마지막 처리기(즉, 처리기가 아닌 catch 첫 번째 문 또는 구문)에서 실행을 다시 시작합니다. 컨트롤은 throw된 예외를 catch 통해서만 처리기를 입력할 수 있으며 문의 문이나 레이블 switchcase 통해 goto 서는 안됩니다.

스택 해제 예제

다음 예제에서는 예외가 throw되면 어떻게 스택이 해제되는지 보여 줍니다. 스레드에서의 실행은 방식에 따라 각 함수를 해제하면서 C의 throw 문에서 main의 catch 문으로 점프합니다. Dummy 개체가 만들어진 다음 범위에서 벗어날 때 제거되는 순서를 살펴보십시오. 또한 catch 문이 포함된 main을 제외하고는 어떤 함수도 완료할 수 없습니다. 함수 AB() 호출에서 반환되지 않으며 BC() 호출에서 반환되지 않습니다. Dummy 포인터의 정의 및 해당 delete 문에 대한 주석 처리를 제거한 다음 프로그램을 실행하면 포인터가 삭제되지 않습니다. 이는 함수가 예외 보장을 제공하지 않는 경우 발생할 수 있음을 보여 줍니다. 자세한 내용은 방법: 예외에 대한 디자인을 참조 하세요. catch 문을 주석으로 처리하는 경우 처리되지 않은 예외로 인해 프로그램을 종료할 때 나타나는 현상을 확인할 수 있습니다.

#include <string>
#include <iostream>
using namespace std;

class MyException{};
class Dummy
{
    public:
    Dummy(string s) : MyName(s) { PrintMsg("Created Dummy:"); }
    Dummy(const Dummy& other) : MyName(other.MyName){ PrintMsg("Copy created Dummy:"); }
    ~Dummy(){ PrintMsg("Destroyed Dummy:"); }
    void PrintMsg(string s) { cout << s  << MyName <<  endl; }
    string MyName;
    int level;
};

void C(Dummy d, int i)
{
    cout << "Entering FunctionC" << endl;
    d.MyName = " C";
    throw MyException();

    cout << "Exiting FunctionC" << endl;
}

void B(Dummy d, int i)
{
    cout << "Entering FunctionB" << endl;
    d.MyName = "B";
    C(d, i + 1);
    cout << "Exiting FunctionB" << endl;
}

void A(Dummy d, int i)
{
    cout << "Entering FunctionA" << endl;
    d.MyName = " A" ;
  //  Dummy* pd = new Dummy("new Dummy"); //Not exception safe!!!
    B(d, i + 1);
 //   delete pd;
    cout << "Exiting FunctionA" << endl;
}

int main()
{
    cout << "Entering main" << endl;
    try
    {
        Dummy d(" M");
        A(d,1);
    }
    catch (MyException& e)
    {
        cout << "Caught an exception of type: " << typeid(e).name() << endl;
    }

    cout << "Exiting main." << endl;
    char c;
    cin >> c;
}

/* Output:
    Entering main
    Created Dummy: M
    Copy created Dummy: M
    Entering FunctionA
    Copy created Dummy: A
    Entering FunctionB
    Copy created Dummy: B
    Entering FunctionC
    Destroyed Dummy: C
    Destroyed Dummy: B
    Destroyed Dummy: A
    Destroyed Dummy: M
    Caught an exception of type: class MyException
    Exiting main.

*/