Tcpl 14장 예외처리

Preview:

DESCRIPTION

the c++ programming language book 14 chapter presentation

Citation preview

14장 예외 처리모임: 아꿈사이재정

목차

목차 있으면 좋겠지만~ 내용이 너무 많군요.

에러를 처리하는 일반적인 방식들

1. 프로그램을 종료하거나2. 에러를 나타내는 값을 반환하거나3. 정상적인 값을 반환하고 비정상적인 상태로 프로그램을 끝내든가, 아니면

4. 에러의 경우에 호출되도록 만들어 둔 함수를 호출한다.

C++ 예외 처리

C++의 예외 처리 메커니즘은 종래의 에러 처리 기법이 불충분하고, 깔끔하지 않으며, 높은 에러 발생률을 보이는 경우에 종래의 기법 대신에 쓸 수 있는 기능이다.

C++ 예외 처리

에러 처리용 코드와 '정상 실행용' 코드가 별도의 영역에서 서로 끼어들지 않고 공존하고 있는 구조이기 때문에 코드 읽기가 더 쉬울 뿐 아니라 개발 도구로 고치기도 쉽다.

C++ 예외 처리의 거추장스러움

C++ 예외 처리가 거추장스러운가?

프로그램에 예외가 사용되면 예외 처리 코드가 도드라지게 복잡해 보이긴 한다. 그러나 예외가 사용되었기 때문에 복잡해지는 것은 절대로 아니다. 패전 소식을 가져온 애꿎은 전령에게 침 튀기지 말라는 이야기다. (스트룹형님)

예외 처리는 이렇게 바라보자

'지금껏 한 번도 생기지 않다가 갑자기 터진것''끔찍한 것'

이것보다 예외는 이렇게 보는 것이 더 정확하다.

'시스템의 특정 부분에 어떤 요청이 들어왔는데 거기서 그것을 처리할 수 없는 경우'

예외 처리 코드를 익히자

1. 예외 처리는 주로 클래스로 만들어지고, 클래스로 던진다. (throw 된다.)

2. 클래스라서 상속도 가능하다.3. 다형성을 구현할 수 있다. 4. 예외 클래스라고 해서 항상 exception 을 상속받을 필요는 없다.

5. 던진(throw) 것을 받는(catch) 코드는 일반적인 함수 인자 처리 메커니즘과 동일하다.

파생된 예외 클래스

예외 처리 클래스의 복사 손실

복사 손실을 막는 법은 레퍼런스로

복합 예외

하나 이상의 예외를 하나로 묶어 통합하는 기술이다.

복합 예외 예제

복합 예외 결과

예외 받아 내기 조건

throw E();catch (H) {...}

1. H와 E가 같은 타입이어야 한다.2. H로부터 public 상속으로 파생된클래스가 E어야 한다.3. H와 E가 포인터 타입이고, H와 E가 가리키는 실제 객체의 타입들이 1, 2 조건을 만족

4. H가 참조자 타입이고, H가 참조하는 실제 객채의 타입이 1 혹은 2 조건을 만족한다.

예외 받아 내기 예제

예외 받아 내기 결과

다른 예제

예외 재전송 (예외 전파)

catch 한 예외를 다시 throw 할 수 있다.재 throw 된 예외는 본래의 타입으로 던져진다.

예외가 던져질 때 객체 복사가 일어난다고 하는데, 테스트 결과 레퍼런스로 던질 때는 복사가 일어 나지 않았다.

예외 처리 전부 받아 내기

try {샬라샬라~

} catch (...) {전부 받아 냅니다.

}

예외 처리자 블록의 등장순서

try {}catch ( A ) {}catch (...) {}catch ( B ) {

Never reach this code}

자원 관리

예외 처리와 자원 관리는 무슨 관계일까?

파일이나, 크리티컬섹션, 메모리등을 사용하고 난 이후에 자원을 해제시켜줘야 한다. 이것을 쉽게 처리하기 위해 스마트 포인터와 같은 기술이 쓰인다.

스마트 포인터는 기본적으로 생성자와 소멸자에서 자원을 관리한다.

자원 관리 방식

생성자와 소멸자는 리턴값이 없기 때문에, 에러 처리 방식이 애매하다.

이 때 예외 처리를 이용하면 간편하다.

생성과 동시에 초기화하는 방식을 "자원획득 즉 초기화" (resource acquisition is initialization)RAII 라고한다.

파일 리소스 관리

파일은 fopen() 함수로 파일을 열고,fclose() 함수로 파일을 닫는다. 실수로 fopen() 후에 닫는 것을 깜빡할 수 있다. 생성자와 소멸자 시스템을 이용해서 자동으로 열고 닫히는 클래스를 만들어 보자.

파일 리소스 관리 클래스

File_ptr 사용 예제

예외 발생시 소멸자 호출

예외가 발생하면, 해당 유효 범위를 벗어날 때, try 블록 안에 만들어진 지역 객체는 모두 없어진다. (소멸자가 호출된다.)

생성자에서 발생한 예외일 경우, 해당 객체는 소멸자가 호출 되지 않는다.

소멸자에서 예외가 발생하면, 소멸자가 호출되지 않는다.

스택 풀기

스택 풀기● 예외가 발생했을 때 예외를 처리해 줄 처리자를 찾아 스택을 거슬러 올라가 탐색하는 과정을 말한다.

● 스택 풀기가 일어나면, 지역적으로 생성된 객체는 모두 다 소멸자가 호출된다.

스택 풀기시 소멸자가 호출 될 때, 다시 예외가 발생하면? terminate() 함수가 호출되어 프로그램이 종료된다.

생성자,소멸자 조심해야 할 부분

생성자나 소멸자에서 예외가 발생하면, 해당 블록에서 생성된 지역객체가 해제되지 않기 때문에 주의해야 한다.

해결법

동적으로 생성되는 변수들은 객체로 관리하자.

std::vector<int> 를 사용하자.

auto_ptr을 사용한 자원 관리

동적으로 할당된 경우는 어떻게 하나?

예외가 발생될 때, 소멸자가 호출되면서 자원을 해제 시켜줄 객체가 필요하다.

auto_ptr<T> 을 쓰자.

auto_ptr<T>

생성자에서 포인터를 할당받고, 소멸자가 호출될 때, delete 가 호출된다. 그 뿐이다.~

같은 포인터를 중복해서 참조하면, delete 가 두번 호출되니 문제가 발생한다.

c++11 에서는 unique_ptr<T>을 쓰라고 조언하고 있다.

위험천만한 auto_ptr<T>

vector< auto_ptr<Shape> > v;sort( v.begin(), v.end() );

어떻게 될까?

auto_ptr<T> 은 스마트 포인터가 아니다.

오버로딩된 new, delete

오버로딩 된 new/delete 오퍼레이터는 어떻게 될까? 문제 없이 잘된다.

컴파일러는 오버로딩 된 new로 할당 받은 객체를 delete 할 때, 동일하게 오버로딩 된 delete 로 호출하게 되어 있다.

vs2008 에서는 new 와 동시에 delete 같이 오버로딩해야 컴파일 에러가 없지만, vs2010부터는 new 나 delete 둘 중 하나만 정의해도 문제가 없다.

placement new 에서 소멸자

원칙적으로 placement new 로 할당 받은 객체를 delete 하는 것은 잘 못된 것이다.

auto_ptr<T> 은 소멸자에서 delete p; 명령어가 실행되기 때문에 placement new로 할당 받았다고 하더라도 delete 가 호출된다.

쉽게 얘기하자면, placement new로 생성된 객체는 auto_ptr<T> 를 쓰면 안된다.

자원 고갈에 대한 대책

대응방안

1. 실행재개2. 실행종료

자원 할당 코드는 여러개의 추상화층으로 구성하고, 호출되는 층이 호출하는 층의 보조를 받지 못하게 하라.

멤버 초기화 식에서 예외 처리

A::A(int s) try : x(s){}catch (...){}

복사 생성자, 대입 연산자 예외 처리

1. 복사 생성자에서 예외가 발생하면 소멸자가 호출 되지 않는다. 메모리 누수가 발생될 수 있기 때문에 주의해야 한다.

2. 대입 연산자에서 예외가 발생하면 소멸자가 호출된다. 그렇기 때문에 소멸자가 호출되어도 문제 없도록 프로그램되어 있어야 한다.

소멸자 안에서 발생하는 예외

소멸자 안에서 예외가 발생하면, 평소와 다름없이 예외가 발생한다.

그러나, 스택 풀기 중에 예외가 발생하면 std::terminate() 함수가 호출되면서 프로그램이 종료된다.

스택 풀기 중에 소멸자가 호출되는지, 정상적으로 소멸자가 호출되는지 알 수 있다.

'에러'가 아닌 예외들

트리를 순회하면서 재귀 함수를 호출할 때, 재귀를 빠져나오는 코드로 throw를 호출할 수도 있다.

빠르긴 하지만, 이상하다.

어지간한 상황이 아니라면 '예외 처리는 에러 처리'라는 자세를 견지했으면 한다. (스트럽)

예외 지정 기능

특정 함수에서 어떤 예외가 던져지는지 지정 할 수 있다.

Visual Studio 는 nothrow 만 지원하고 있다.

예외 지정이 발생 시키는 것들 요약

지정한 예외가 아닌 다른 예외를 던지면 std::unexpected() 함수가 호출된다.

set_unexpected() 로 핸들러를 설정할 수 있으며, 이 핸들러에서 새 예외 처리를 throw 하면 원하는 예외 처리를 할 수 있다.

예외 처리 효율

예외 처리를 구현하기 위한 내부 시스템으로 인해 속도가 느려질 수 있지만, 구조적이고, 조직적인 에러 처리가 필요하다면 예외 처리를 사용하는 것이 이득이다.

표준 예외 처리

표준 예외를 많이 만든 이유는, 이것들을 기반으로 하여 다른 C++ 예외 클래스를 만들라는 배려로 보면 되겠다.

바른 프로그래밍을 위한 고수의 조언

1. 에러 처리에는 예외를 사용하자.2. 지역적 흐름 제어 구조로도 충분한 곳에는 예외를 사용하지 말 것.3. 자원 관리에는 "자원획득 즉 초기화"(Resource Acquisition is Initialization, RAII) 기법을 사용하자.4. 모든 프로그램에 예외 안전성을 부여할 필요는 없다.5. 불변속성을 유지하는 데도 "자원획득 즉 초기화" 기법 및 예외 처리자를 사용하자.6. try 블록의 사용은 최대한 자제하자, 직접 에러 처리자 코드를 두기 보다는 "자원획득 즉 초기화" 방법을 적극적으로 활용하자.7. 모든 함수에서 모든 가능한 에러를 도맡아 처리할 필요는 없다.8. 생성자가 수행되다 오동작이 나면, 예외를 던져서 이것을 알릴 것.9. 대입 연산 중에 예외를 던지기 전에, 모든 연산자가 유효한 상태에 있도록 하자.10. 소멸자에서는 예외 발생을 피하자.

바른 프로그래밍을 위한 고수의 조언

11. 모든 예외를 받아 보고하는 코드는 main()에 두도록.12. 에러 처리와 상관 없는 코드와 에러 처리 코드는 격리 시킬 것.13. 생성자에서 예외를 던지려 할 때 미리 생성자에서 자원이 획득된 상태라면, 해당 자원을 모두 해제하자.14. 자원 관리는 단계적으로 진행되도록 하자.15. 주로 사용하게 될 인터페이스에 대해서는 예외 지정 기능을 사용하자.16. new에 의해 할당되었는데 예외 발생으로 인해 해제되지 않은 메모리가 메모리 누수를 일으킨다는 사실을 잊지말자.17. 함수에서 발생할 수 있는 예외는 언젠가 발생할 것이란 가정을 항상 빠뜨리지 말도록.18. 무릇 예외라면 exception 클래스에서 파생되어야 한다는 편견을 버리자.19. 라이브러리는 자신의 판단만으로 바로 프로그램을 끝내 버리게 만들면 안된다. 예외를 발생시켜 라이브러리 호출자에게 판단을 넘기자.20. 라이브러리는 최종 사용자를 염두에 둔 진단 출력 메시지를 만들어서도 안된다. 예외를 발생시켜 호출자에게 판단을 넘기자.21. 예외 처리 전략은 설계 단계에서 세우자.

수고하셨습니다.

Recommended