Language/C++
[C++] 31.Unmanaged Programming: Exception
coco_daddy
2024. 1. 31. 00:48
1. 예외 처리 사용에 대한 주의
- 남용해서는 안 된다.
- 대부분의 예외는 불필요하다. (생성자의 경우에는 유용)
2. 예외를 사용해야 하는 상황
- 나 이외의 문제로 발생하는 예외이기 때문에 control 할 수 없는 경우
3. 사람의 사고 방식은 선형적
- 사람은 순서대로 읽는다.
- 여러 문제를 한 번에 처리하려고 하면 필연적으로 실수가 생긴다.
4. 언어 자체적으로 주는 예외는 없다.
- 프로그래머가 직접 만든 것.
- 표준 라이브러리에서 예외를 던져주기는 한다. (
std::exception
클래스를 상속받아 사용)
5. 예외상황이 아닌 예외 상황
- 범위(range) 이탈
#include <exception>
int main()
{
std::string catName = "Coco"
try
{
char ch = myCatName.at(5)
}
catch (const std::out_of_range& e)
{
std::cerr << "out of range: " << e.what() << std::endl;
}
catch (const std::exception& e) // out_of_range가 받지 못한 예외를 여기서 모두 받겠다.
{
std::cerr << "exeption: " << e.what() << std::endl;
}
}
- 순서대로 catch함
- 사실 if문으로 해결할 수 있음.
if (index < catName.size())
{
char ch = catName.at(index)
}
6. 0으로 나누기
- 컴파일러가 잡아주거나 OS가 잡아준다.
- 표준 라이브러리에서는 정의되지 않은 결과로 남겨둠.
- 컴파일러 설계에 따라 다르다.
- 인터럽트 기반의 예외는 느리다. (컨텍스트 스위칭 때문)
7. NULL 개체 참조
- C++에서는 정의되어 있지 않기 때문에 OS가 인터럽트 방식으로 시그널을 발생시킴.
- 예외라고 생각하기 힘들다.
8. OS의 예외와 C++의 예외
9. 이론적으로 생성자의 경우에는 예외가 필요하다.
- 생성자에서 예외를 다루는 방법
- 자체 예외를 던지는 방법
Inventory::Inventory(int count)
{
mSlots = new int[count];
}
- mSlot가 NULL인 경우.
- 생성자는 반환값이 없어 체크를 할 수 없다.
- 실패 시 NULL을 넣어줄 수 없다.
#include <exception>
struct SlotNullException : public std::exception
{
const char* what() const throw ()
{
return ("Slot is NULL");
}
}
- what()은 무슨 예외인지 알려주는 string을 반환한다.
Inventory::Inventory(int count)
{
mSlots = new int[count];
if (mSlots == NULL)
{
throw SlotNullException();
}
}
- throw된 예외는 아래와 같이 처리한다.
❇︎중요 참고사항
일반적으로 C++를 사용하는 업계는 예외처리 기능을 활성화하지 않는다.
많은 C++ 컴파일러들이 성능을 위해 이 기능을 사용하지 않도록 되어있다.
메모리 부족으로 인한 예외 발생
- 프로그램 종료
- 크래시와 다를 것이 없음
- 생성자 재호출
- 메모리가 부족해서 개체를 생성할 수 없었던 것이기 때문에 의미가 없음.
- 모든 생성자마다 NULL이 나올 가능성을 생각해서 NULL 핸들링을 하는 것은 비효율적이다.
> 프로그램이 터지도록 두는 것이 나을 수 있음.
10. 예외 안정성
- 예외가 나도 프로그램이 계속 돌아가야 한다.
- 예외는 프로그램을 좀비로 만든다.
- 살아는 있는데, 정상 작동이라고 볼 수 없다.
11. 안 좋은 예와 좋은 예 비교
class CoffeeShop {
void SellWithPoint(Customer customer, int itemID, int points) throws EmptyItemException {
deductPoint(customer, points);
if (isEmpty(itemID)) {
throw new EmptyItemException();
}
}
};
포인트는 감소가 되었는데, 커피는 주지 못했다.
class CoffeeShop {
void SellWithPoint(Customer customer, int itemID, int points) throws EmptyItemException {
if (isEmpty(itemID)) {
throw new EmptyItemException();
}
deductPoint(customer, points);
}
};
→ early exit 방식으로 구현하면 된다..!
→ 이론적으로는 훌륭하지만 100% 예외 안전성을 가지는 프로그램을 짜는 것이 쉬운 일이 아니다.
12. 예외 처리 사용에 대한 결론
- 사람은 무엇을 읽어도 위에서부터 아래로 읽는다.
- 100% 예외 안전성을 가지는 프로그램을 짜는 것이 어렵다.
- 예외 처리는 필요한 경우에만 사용하는 것이 좋다.