ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄/C++ 기초

[C++] 17. μ˜ˆμ™Έμ²˜λ¦¬(Exception), std::exception

μ„œμ•„λž‘πŸ˜ 2023. 7. 23. 23:28

 

 

μ˜ˆμ™Έμ²˜λ¦¬(Exception)

C++μ—μ„œ μ˜ˆμ™Έμ²˜λ¦¬λŠ” ν”„λ‘œκ·Έλž¨μ˜ μ‹€ν–‰ 쀑에 λ°œμƒν•˜λŠ” μ˜ˆμ™Έ 상황을 κ°μ§€ν•˜κ³  μ μ ˆν•œ 쑰치λ₯Ό μ·¨ν•˜λŠ” λ©”μ»€λ‹ˆμ¦˜μž…λ‹ˆλ‹€. 이λ₯Ό 톡해 ν”„λ‘œκ·Έλž¨μ˜ μ•ˆμ •μ„±μ„ 높이고 μ˜ˆμƒμΉ˜ λͺ»ν•œ λ¬Έμ œμ— λŒ€μ²˜ν•  수 있게 λ©λ‹ˆλ‹€. μ˜ˆμ™ΈλŠ” 일반적으둜 ν”„λ‘œκ·Έλž¨μ΄ μ‹€ν–‰ 쀑에 λ°œμƒν•˜λŠ” 였λ₯˜ λ˜λŠ” μ˜ˆμƒμΉ˜ λͺ»ν•œ 상황을 λ‚˜νƒ€λƒ…λ‹ˆλ‹€. μ΄λŸ¬ν•œ 상황을 μ²˜λ¦¬ν•˜λŠ” 데 μ‚¬μš©λ˜λŠ” 기본적인 C++ μ˜ˆμ™Έ 처리 λ©”μ»€λ‹ˆμ¦˜μ— λŒ€ν•΄ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.

 

μ˜ˆμ™Έ λ˜μ§€κΈ° (Throwing Exceptions)

μ˜ˆμ™Έλ₯Ό 던질 λ•ŒλŠ” throw ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.
throw ν‚€μ›Œλ“œ λ’€μ—λŠ” μ˜ˆμ™Έ κ°μ²΄κ°€ μ˜΅λ‹ˆλ‹€. μΌλ°˜μ μœΌλ‘œ μ˜ˆμ™Έ κ°μ²΄λŠ” κΈ°λ³Έ λ°μ΄ν„° νƒ€μž…μ΄λ‚˜ μ‚¬μš©μž μ •μ˜ ν΄λž˜μŠ€ κ°μ²΄μΌ μˆ˜ μžˆμŠ΅λ‹ˆλ‹€.

void doSomething(int value) {
    if (value < 0) {
        throw "Value cannot be negative!"; // λ¬Έμžμ—΄μ„ μ˜ˆμ™Έλ‘œ 던짐
    }
}

 

 

μ˜ˆμ™Έ λ‹€μ‹œ λ˜μ§€κΈ° (Rethrowing Exceptions)

ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ μ²˜λ¦¬ν•œ μ˜ˆμ™Έλ₯Ό μ™ΈλΆ€λ‘œ λ‹€μ‹œ 던질 수 μžˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό μ˜ˆμ™Έ λ‹€μ‹œ λ˜μ§€κΈ°λΌκ³  ν•©λ‹ˆλ‹€.
μ΄λŠ” μ€‘μ²©λœ try-catch λΈ”λ‘μ—μ„œ μ‚¬μš©λ˜λ©°, ν˜„μž¬ ν•¨μˆ˜μ˜ catch λΈ”둝이 μ˜ˆμ™Έλ₯Ό μ²˜λ¦¬ν•  μˆ˜ μ—†λŠ” κ²½μš° μƒμœ„ ν˜ΈμΆœμžλ‘œ μ˜ˆμ™Έλ₯Ό μ „λ‹¬ν•˜λŠ” λ° μ‚¬μš©λ©λ‹ˆλ‹€.

void doSomething() {
    try {
        // μž‘μ—… μˆ˜ν–‰
    } catch (const SomeException& e) {
        // μ˜ˆμ™Έ 처리
        throw; // ν˜„μž¬ μ˜ˆμ™Έλ₯Ό λ‹€μ‹œ 던짐
    }
}

 

 

μ‚¬μš©μž μ •μ˜ μ˜ˆμ™Έ 클래슀 (Custom Exception Classes)

C++μ—μ„œλŠ” κΈ°λ³Έ 데이터 νƒ€μž… 외에도 μ‚¬μš©μž μ •μ˜ μ˜ˆμ™Έ 클래슀λ₯Ό λ§Œλ“€μ–΄ μ˜ˆμ™Έλ₯Ό 던질 수 μžˆμŠ΅λ‹ˆλ‹€. 보톡 μ‚¬μš©μž μ •μ˜ μ˜ˆμ™Έ ν΄λž˜μŠ€λŠ” std::exception ν΄λž˜μŠ€λ₯Ό μƒμ†λ°›μ•„μ„œ μž‘μ„±ν•©λ‹ˆλ‹€.

class MyException : public std::exception {
public:
    const char* what() const noexcept override {
        return "This is a custom exception!";
    }
};

void doSomething() {
    throw MyException();
}

 

 

 

std::exception

C++ ν‘œμ€€ λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œλŠ” μ˜ˆμ™Έ μ²˜λ¦¬λ₯Ό μœ„ν•΄ std::exception ν΄λž˜μŠ€λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€. μ΄ ν΄λž˜μŠ€λŠ” C++ ν‘œμ€€ μ˜ˆμ™Έ ν΄λž˜μŠ€λ“€μ˜ κΈ°λ³Έ ν΄λž˜μŠ€λ‘œ μ‚¬μš©λ©λ‹ˆλ‹€. std::exception ν΄λž˜μŠ€λŠ” <exception> ν—€λ” νŒŒμΌμ— μ •μ˜λ˜μ–΄ μžˆμœΌλ©°, λ‹€λ₯Έ μ˜ˆμ™Έ ν΄λž˜μŠ€λ“€μ€ μ΄ ν΄λž˜μŠ€λ₯Ό μƒμ†λ°›μ•„ κ΅¬ν˜„λ©λ‹ˆλ‹€.

std::exception ν΄λž˜μŠ€λŠ” κ°€μž₯ κ°„λ‹¨ν•œ μ˜ˆμ™Έ μ²˜λ¦¬λ₯Ό μœ„ν•œ κΈ°λ°˜ ν΄λž˜μŠ€λ‘œμ„œ, μΌλ°˜μ μœΌλ‘œ μ˜ˆμ™Έμ— λŒ€ν•œ μ •λ³΄λ₯Ό μ œκ³΅ν•˜κΈ° μœ„ν•΄ what() λ©€λ²„ ν•¨μˆ˜λ₯Ό κ°€μ§€κ³  μžˆμŠ΅λ‹ˆλ‹€. μ΄ ν•¨μˆ˜λŠ” μ˜ˆμ™Έμ— λŒ€ν•œ μ„€λͺ…을 C μŠ€νƒ€μΌμ˜ λ¬Έμžμ—΄λ‘œ λ°˜ν™˜ν•˜λ©°, νŒŒμƒ ν΄λž˜μŠ€λ“€μ€ μ΄ λ©€λ²„ ν•¨μˆ˜λ₯Ό μ˜€λ²„λΌμ΄λ“œν•˜μ—¬ μžμ‹ λ§Œμ˜ μ˜ˆμ™Έ μ •λ³΄λ₯Ό λ°˜ν™˜ν•  μˆ˜ μžˆμŠ΅λ‹ˆλ‹€.

std::exception의 μ •μ˜λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€:

namespace std {
    class exception {
    public:
        exception() noexcept;
        exception(const exception&) noexcept;
        exception& operator=(const exception&) noexcept;
        virtual ~exception();

        virtual const char* what() const noexcept;
    };
}

 

what() 멀버 ν•¨μˆ˜

const char* what() const noexcept

이 ν•¨μˆ˜λŠ” μ˜ˆμ™Έμ— λŒ€ν•œ μ„€λͺ…을 λ°˜ν™˜ν•©λ‹ˆλ‹€. 기본적으둜 이 ν•¨μˆ˜λŠ” "std::exception"을 λ°˜ν™˜ν•˜μ§€λ§Œ, νŒŒμƒ ν΄λž˜μŠ€μ—μ„œ 이 ν•¨μˆ˜λ₯Ό μ˜€λ²„λΌμ΄λ“œν•˜μ—¬ 더 ꡬ체적인 정보λ₯Ό μ œκ³΅ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

#include <iostream>
#include <exception>

class MyException : public std::exception {
public:
    const char* what() const noexcept override {
        return "This is a custom exception!";
    }
};

int main() {
    try {
        throw MyException();
    } catch (const std::exception& e) {
        std::cout << "Caught exception: " << e.what() << std::endl;
    }
    return 0;
}

// 좜λ ₯:
// Caught exception: This is a custom exception!

 

std::exception ν΄λž˜μŠ€λ₯Ό μ§μ ‘ μ‚¬μš©ν•˜κΈ°λ³΄λ‹€λŠ”, μ΄λ₯Ό μƒμ†λ°›μ•„μ„œ μ‚¬μš©μž μ •μ˜ μ˜ˆμ™Έ ν΄λž˜μŠ€λ₯Ό λ§Œλ“€μ–΄μ„œ λ” κ΅¬μ²΄μ μΈ μ˜ˆμ™Έ μ •λ³΄λ₯Ό μ œκ³΅ν•˜λŠ” κ²ƒμ΄ μΌλ°˜μ μž…λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•¨μœΌλ‘œμ¨ μ˜ˆμ™Έ μ²˜λ¦¬ μ½”λ“œμ—μ„œ λ” μœ μš©ν•œ μ •λ³΄λ₯Ό μ–»μ„ μˆ˜ μžˆκ²Œ λ©λ‹ˆλ‹€.

 

std::exception을 μƒμ†λ°›λŠ” 클래슀

 

 

 

μ˜ˆμ™Έλ₯Ό λ‹€μ–‘ν•˜κ²Œ μ²˜λ¦¬ν•˜κΈ°

μ˜ˆμ™Έμ²˜λ¦¬λŠ” μ‹œμŠ€ν…œ μ˜ˆμ™Έ 처리 μ˜μ—­κ³Ό Standard library의 μ˜ˆμ™Έ 처리 μ˜μ—­μœΌλ‘œ λ‚˜λˆŒ 수 μžˆμŠ΅λ‹ˆλ‹€. μš°λ¦¬κ°€ μ½”λ“œλ₯Ό μ§œλ©΄μ„œ λ°œμƒ ν•  수 μžˆλŠ” λͺ¨λ“  μ—λŸ¬μ— λŒ€ν•΄ μ˜ˆμ™Έμ²˜λ¦¬λ₯Ό ν•  수 μ—†μŠ΅λ‹ˆλ‹€. κ·Έλ ‡μ§€λ§Œ λ‹€μ–‘ν•œ μ˜μ—­κ³Ό 쑰건을 κΈ°μ€€μœΌλ‘œ μ˜ˆμ™Έλ₯Ό μ •μ˜ν•œλ‹€λ©΄ μ˜ˆμ™Έλ₯Ό λ§ˆμ£Όν–ˆμ„ λ•Œ λ”μš± μœ μ—°ν•˜κ²Œ λŒ€μ²˜ν•  수 μžˆμ„ κ²ƒμž…λ‹ˆλ‹€. 

 

λ‹€μ–‘ν•œ μ˜ˆμ™Έ μœ ν˜• μ •μ˜ν•˜κΈ°

μ—¬λŸ¬ μ’…λ₯˜μ˜ μ˜ˆμ™Έλ₯Ό 닀루어야 ν•˜λŠ” 경우, 각각의 상황에 μ ν•©ν•œ μ‚¬μš©μž μ •μ˜ μ˜ˆμ™Έ 클래슀λ₯Ό λ§Œλ“€μ–΄μ„œ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό 톡해 μ˜ˆμ™Έμ˜ μœ ν˜•μ„ λͺ…ν™•ν•˜κ²Œ κ΅¬λΆ„ν•˜κ³  ꡬ체적인 μ˜ˆμ™Έ 처리λ₯Ό μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

class FileOpenException : public std::exception {
public:
    const char* what() const noexcept override {
        return "Failed to open the file!";
    }
};

class DivideByZeroException : public std::exception {
public:
    const char* what() const noexcept override {
        return "Division by zero is not allowed!";
    }
};

 

 

μ˜ˆμ™Έ λ°œμƒ 쑰건 λΆ„λ¦¬ν•˜κΈ°

μ—¬λŸ¬ κ³³μ—μ„œ μ˜ˆμ™Έκ°€ λ°œμƒν•  수 μžˆλŠ” 경우, μ˜ˆμ™Έκ°€ λ°œμƒν•˜λŠ” 쑰건을 λΆ„λ¦¬ν•΄μ„œ μ²˜λ¦¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ μ˜ˆμ™Έ 처리 μ½”λ“œλ₯Ό 각각의 μ μ ˆν•œ μœ„μΉ˜μ— 두어 μ½”λ“œμ˜ 가독성을 높일 수 μžˆμŠ΅λ‹ˆλ‹€.

void divideNumbers(int a, int b) {
    if (b == 0) {
        throw DivideByZeroException();
    }
    // λ‚˜λ¨Έμ§€ μ—°μ‚° μˆ˜ν–‰
}

void readFile(const std::string& filename) {
    std::ifstream file(filename);
    if (!file.is_open()) {
        throw FileOpenException();
    }
    // 파일 읽기 μˆ˜ν–‰
}

 

 

μ˜ˆμ™Έ μ²˜λ¦¬μ™€ λ¦¬μ†ŒμŠ€ 관리

λ¦¬μ†ŒμŠ€ κ΄€λ¦¬λŠ” μ˜ˆμ™Έ μ•ˆμ „μ„±κ³Ό λ°€μ ‘ν•œ 관련이 μžˆμŠ΅λ‹ˆλ‹€. μ˜ˆμ™Έκ°€ λ°œμƒν•˜λ”λΌλ„ μžμ›μ΄ μ˜¬λ°”λ₯΄κ²Œ κ΄€λ¦¬λ˜λ„λ‘ 슀마트 포인터와 RAII(Resource Acquisition Is Initialization)λ₯Ό ν™œμš©ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

void someFunction() {
    std::unique_ptr<int> ptr(new int(42)); // 슀마트 ν¬μΈν„°λ‘œ μžμ›μ„ ν• λ‹Ή
    // μž‘μ—… μˆ˜ν–‰
    // μ˜ˆμ™Έκ°€ λ°œμƒν•˜λ”λΌλ„ ptr이 μžλ™μœΌλ‘œ λ©”λͺ¨λ¦¬λ₯Ό ν•΄μ œν•¨
}

 

 

μ˜ˆμ™Έ μ•ˆμ „μ„±κ³Ό νŠΈλžœμž­μ…˜

μ˜ˆμ™Έ μ²˜λ¦¬λŠ” νŠΈλžœμž­μ…˜κ³Ό μœ μ‚¬ν•œ κ°œλ…μ„ μ μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ˜ˆμ™Έκ°€ λ°œμƒν•˜λ©΄ ν•΄λ‹Ή μž‘μ—…μ΄ λ‘€λ°±λ˜λ„λ‘ κ΅¬ν˜„ν•˜μ—¬ ν”„λ‘œκ·Έλž¨μ˜ 일관성을 μœ μ§€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

class BankAccount {
private:
    int balance;

public:
    void deposit(int amount) {
        // μ˜ˆμ™Έ λ°œμƒ μ‹œ μž„μ‹œ λ³€μˆ˜μ— κΈ°μ‘΄ μž”μ•‘μ„ μ €μž₯ν•˜μ—¬ λ‘€λ°± κ°€λŠ₯ν•˜κ²Œ 함
        int tempBalance = balance;
        try {
            // μž”μ•‘ λ³€κ²½ μž‘μ—… μˆ˜ν–‰
            balance += amount;
        } catch (...) {
            // μ˜ˆμ™Έ 처리 ν›„ μž”μ•‘μ„ 이전 κ°’μœΌλ‘œ λ‘€λ°±
            balance = tempBalance;
            throw; // μ˜ˆμ™Έλ₯Ό λ‹€μ‹œ λ˜μ Έμ„œ μƒμœ„ ν˜ΈμΆœμžμ—κ²Œ μ „νŒŒ
        }
    }
};