C++ try-catch 예외처리에서 catch 이후 프로그램이 종료되지 않게 하려면 어떻게 해야할까요?(초보자입니다.)

조회수 5536회

아래 코드의 cout << B.top() << endl; 에서 예외가 발생하여 예외가 발생했다는 문장을 출력합니다. 그 이후 바로 아래줄인 cout << B.size() << endl; 은 실행되지 않습니다. 이 두 문장의 순서를 바꾸면 잘 작동되지만 현재는 top 이후의 문장은 출력되지 않습니다. 아마 예외처리 후 바로 프로그램이 종료되는듯 싶은데 예외처리 후에도 프로그램이 돌아가게 하려면 어떻게 해야할까요?

void main() {

    try {
        ArrayStack<int> A;
        A.push(7);
        A.push(13);
        cout << A.top() << endl;
        A.push(9);
        cout << A.top() << endl;
        cout << A.top() << endl;
        ArrayStack<string> B(10);
        B.push("Bob");
        B.push("Alice");
        cout << B.top() << endl;
        B.push("Eve");
        A.pop();
        A.pop();
        B.pop();
        B.pop();
        B.pop();
        cout << B.top() << endl;
        cout << B.size() << endl;
    }
    catch (StackEmpty& obj) {
        cout << obj.getMessage() << endl;
    }
    catch (StackFull& obj) {
        cout << obj.getMessage() << endl;
    }
}

  • (•́ ✖ •̀)
    알 수 없는 사용자

3 답변

  • 일단 void ArrayStack<E>::pop() throw(StackEmpty) 에서처럼 함수에 throw 특수화를 붙일 필요는 없습니다. 예외 사양(throw)(C++) 문서를 참고해 보세요.

    template <typename E>
    const E& ArrayStack<E>::top() const throw(StackEmpty) {
        try {
            if (isEmpty()) throw StackEmpty("Stack is empty!");
            return S[t];
        }
        catch (StackEmpty& obj) {
            cout << obj.getMessage() << endl;
            exit(1); // exit(1)이 없으면 실행중에 에러가 발생해버림..
            // 왜 에러가 발생할까요? return은 어떻게 하죠?
        }
        // 예외를 발생시킬 함수 내부에서 이렇게 예외처리까지 넣어버리는 것은 좋지 않습니다.
        // 이 함수를 사용하는 쪽에서 예외를 처리하도록 하는 게 맞습니다.
    }
    
    template <typename E>
    const E& ArrayStack<E>::top() const
    {
        if (!isEmpty())
            return S[t];
    
        throw StackEmpty("Stack is empty!");
        // 예외가 throw될 때는, return 값을 반환할 수 없습니다.
        // 이 함수를 호출하는 쪽에서 반환값을 받지 않고 catch문으로 이동하기 때문입니다.
        // 그런데 적절한 catch문이 없다면 문제가 생기겠지요?
    }
    

    좋은 예시가 있으니 참고해보세요. Throw exception and return result from a function

    호출부분에서 이렇게 처리하면 되지 않을까 싶습니다.

    void main()
    {
        ArrayStack<int> A;
        ArrayStack<string> B(10);
        try {
            A.push(7);
            A.push(13);
            cout << A.top() << endl;
            A.push(9);
            cout << A.top() << endl;
            cout << A.top() << endl;
            B.push("Bob");
            B.push("Alice");
            cout << B.top() << endl;
            B.push("Eve");
            A.pop();
            A.pop();
            B.pop();
            B.pop();
            B.pop();
            cout << B.top() << endl;
        }
        catch (const RuntimeException& e) {
            cout << e.getMessage() << endl;
        }
        cout << B.size() << endl;
    }
    

    +추가 질문에 대한 답변입니다.

    template <typename E>
    const E& ArrayStack<E>::top() const
    {
        if (isEmpty())
            throw StackEmpty("Stack is empty!");
    
        return S[t];
    }
    
    template <typename E>
    void ArrayStack<E>::push(const E& e)
    {
        if (size() >= capacity)
            throw StackFull("Stack is full!");
    
        S[++t] = e;
    }
    
    template <typename E>
    void ArrayStack<E>::pop()
    {
        if (isEmpty())
            throw StackEmpty("Stack is empty!");
    
        t--;
    }
    
    • 감사합니다. MSDN문서들을 읽기에는 아직 좀 어렵네요.ㅠㅠ 반복해서 읽어봐야겠어요.. 그런데 올려주신 코드를 실행 시켰을때 왜 Stack empty가 아니라 Stack is full문장과 0만 나오는지 모르겠네요. top과 size한 결과들이 다 출력되고 예외처리문장도 나오고 그 상태로 계속 프로그램이 돌았으면 하는데 어렵군요 ㅎㅎ 알 수 없는 사용자 2016.10.2 14:17
    • 아마 함수들에 대한 수정을 좀 다르게 하신 듯 하네요. 위의 추가된 코드들을 참고하셔서 다시 수정해보세요. Subin Park 2016.10.2 15:50
  • 애초에 try문은 내부 코드에서 예외가 발생하면, try의 범위 내의 남은 코드들을 실행하지 않는 게 목적입니다. 왜냐면, 해당 예외로 인해 남은 코드들이 제대로 작동하지 않을 가능성이 크기 때문입니다.

    문제 발생의 여지가 있는 코드를 안전하게 실행하고 싶을 때, 해당 코드 블럭을 try로 감싸고, 해당 문제를 해결하거나 로그를 남기는 코드를 catch에 작성하여, 문제가 발생한 경우를 대비하기 위한 장치인 것입니다.

    따라서 try 내에서 예외가 발생하면, 해당 try 내의 남은 코드들은 건너뛰고, 발생한 예외를 처리할 수 있는 catch문으로 이동합니다. catch문이 다 실행되고나면, 그 해당 try-catch문 이후의 코드들은 다시 정상적으로 실행됩니다.

    즉, 걱정하신 것처럼 catch 이후에 프로그램이 종료되는 것은 아닙니다.

    void main() {
    
        try {
            ArrayStack<int> A;
            A.push(7);
            A.push(13);
            cout << A.top() << endl;
            A.push(9);
            cout << A.top() << endl;
            cout << A.top() << endl;
            ArrayStack<string> B(10);
            B.push("Bob");
            B.push("Alice");
            cout << B.top() << endl;
            B.push("Eve");
            A.pop();
            A.pop();
            B.pop();
            B.pop();
            B.pop();
            cout << B.top() << endl;    // 예외발생(스택이 비어있는데 내부 값을 읽으려고 했기 때문)
            cout << B.size() << endl;   // 건너뜀(위에서 예외가 발생했기 때문)
        }
        catch (StackEmpty& obj) {       // 발생한 예외를 catch
            cout << obj.getMessage() << endl;    // 처리
        }
        catch (StackFull& obj) {        // 이 예외는 발생하지 않았으니 건너뜀
            cout << obj.getMessage() << endl;
        }
        // 이 아래의 코드들은 정상적으로 실행됨
    
    }
    

    그런데, 만약 발생한 예외를 처리할 수 있는 catch문이 없다면, 문제가 달라집니다.

    일단 try-catch문 이후의 코드들도 실행하지 않게되며, 만약 위 코드처럼 main함수에서의 try-catch문이었다면, 프로그램이 비정상 종료됩니다.

    하지만, 어떤 함수 내에서의 try-catch문이었고, 만약 그 함수 바깥쪽에서 해당 예외를 잡을 수 있는 catch문이 있다면, 해당 catch문으로 이동하게 됩니다.

    void SomeFunction()
    {
        // do something... (4)
        try {
            // do something... (5)
            // 예외 발생 (6)
            // do something... (x)
        }
        // 해당 예외를 받을 catch가 없음.
        // do something... (x)
    }
    
    int main()
    {
        // do something... (1)
        try {
            // do something... (2)
            SomeFunction(); // (3)
            // do something... (x)
        }
        catch (...) {
            // do something... (7)
        }
        // do something... (8)
        return 0;
    }
    

    좀 더 자세한 내용은 MSDN의 문서를 참고하세요. (차근히 읽어보면 어렵지 않습니다.)

    1. Try, Throw 및 Catch 문(C++)
    2. C++에서 예외 및 스택 해제
    3. 처리되지 않은 C++ 예외
    • 이번에도 너무나도 깔끔하고 좋은 설명 감사합니다 ㅎㅎ 질문 하나 드려도 괜찮을지요. 마지막에 있는 코드부분에서 (6) 이후 바깥 쪽의 catch인 (7)로 이동했으면 SomeFunction 함수 내에 (x) 표시 되어있는 모든 부분은 예외 발생시 실행되지 않는 건가요? 반대로, 만약 예외가 발생되지 않는다면 (5), (6) 이후 그 다음줄 (x)와 그 밑의 do something이 실행되나요? 알 수 없는 사용자 2016.10.1 23:50
    • 네 맞습니다. 그리고, 만약 SomeFunction 함수 내에서 catch문으로 해당 예외가 처리된다면, 바깥의 (7)부분은 건너뛰고, (8)로 이동하게 됩니다. (바깥에서는 해당 함수가 예외가 발생하지 않은 것으로 판단하는 것이죠). 그리고 (x)로 써놓은 부분들은 예외가 발생하지 않은 경우 전부 정상적으로 실행되는 부분들입니다. Subin Park 2016.10.1 23:53
  • 아아 그리고 또 질문있어요!

    1. try- catch문을 main함수 말고 이런식으로 클래스 안으로 넣는게 더 좋을까요? 아니면 main 함수에 두는게 좋을까요?
    2. 아래 코드를 실행시켰을때 B.size()의 값으로 0이 나와야하는데 그 이전 line의 예외가 발생한 줄인 B.top() 까지만 실행되고 B.size()의 값은 전혀 나오지가 않고 프로그램이 종료되어버립니다. exit(1)을 없애면 실행도중에 프로그램이 죽는 에러가 발생해서 일단 달아두긴 했는데 ..암튼 B.size 값도 나오게 하려면 어떻게 해야할까요? 어디가 잘 못된 부분인지 넘나 어렵네요 ㅠㅠ
    #include <iostream>
    #include <string>
    using namespace std;
    
    class RuntimeException {
    private:
        string errMsg;
    public:
        RuntimeException(const string& err) : errMsg(err) {
        }
        string getMessage() const { 
            return errMsg; 
        }
    };
    
    class StackEmpty : public RuntimeException {
    public:
        StackEmpty(const string& err) : RuntimeException(err) {
        }
    };
    
    class StackFull : public RuntimeException {
    public:
        StackFull(const string& err) : RuntimeException(err) {
        }
    };
    
    template <typename E>
    class ArrayStack {
    private:
        E* S;          
        int capacity;
        int t; 
    public:
        ArrayStack(int cap = 100);
        ~ArrayStack();
        int size() const;
        bool isEmpty() const;
        const E& top() const throw(StackEmpty);
        void push(const E& e) throw(StackFull);
        void pop() throw(StackEmpty);
    };
    
    template <typename E>
    ArrayStack<E>::ArrayStack(int cap) {
        capacity = cap;
        t = -1;
        S = new E[cap];
    }
    
    template <typename E>
    ArrayStack<E>::~ArrayStack() {
        delete[] S;
    }
    
    template <typename E>
    int ArrayStack<E>::size() const {
        return t + 1;
    }
    
    template <typename E>
    bool ArrayStack<E>::isEmpty() const {
        return (t == -1);
    }
    
    template <typename E>
    const E& ArrayStack<E>::top() const throw(StackEmpty) {
        try {
            if (isEmpty()) throw StackEmpty("Stack is empty!");
            return S[t];
        }
        catch (StackEmpty& obj) {
            cout << obj.getMessage() << endl;
            exit(1); // exit(1)이 없으면 실행중에 에러가 발생해버림..
        }
    }
    
    template <typename E>
    void ArrayStack<E>::push(const E& e) throw(StackFull) {
        try {
            if (size() == capacity) throw StackFull("Stack is full!");
            S[++t] = e;
        }
        catch (StackFull& obj) {
            cout << obj.getMessage() << endl;
            exit(1); // exit(1)이 없으면 실행중에 에러가 발생해버림..
        }
    }
    
    template <typename E>
    void ArrayStack<E>::pop() throw(StackEmpty) {
        try {
            if (isEmpty()) throw StackEmpty("Stack is empty!");
            t--;
        }
        catch (StackEmpty& obj) {
            cout << obj.getMessage() << endl;
            exit(1); // exit(1)이 없으면 실행중에 에러가 발생해 버림..
        }
    }
    
    void main() {
            ArrayStack<int> A;
            A.push(7);
            A.push(13);
            cout << A.top() << endl;
            A.push(9);
            cout << A.top() << endl;
            cout << A.top() << endl;
            ArrayStack<string> B(10);
            B.push("Bob");
            B.push("Alice");
            cout << B.top() << endl;
            B.push("Eve");
            A.pop();
            A.pop();
            B.pop();
            B.pop();
            B.pop();
            cout << B.top() << endl;
            cout << B.size() << endl;
    }
    
    
    
    • (•́ ✖ •̀)
      알 수 없는 사용자
    • 위 예에서 push, pop 함수에서는 예외가 발생한 것임으로, throw만 해주면 됩니다. 이 함수를 사용하는 곳 - 본 질문의 예에서는 main 함수 - 에서 try~catch를 사용해야 합니다. 어떤 함수가 예외가 발생할 수 있음을 알고 있을 때, 그 예외로 인해 예기치 못한 종료를 막기 위해서 사용하는 것이 try ~ catch 예최처리 구문 입니다. 허대영(소프트웨어융합대학) 2016.10.2 15:28

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)