Chapter 11. 예외처리와템플릿 박종혁교수 UCS Lab Tel: 970-6702 Email: jhpark1@seoultech.ac.kr SeoulTech 2016-2 nd 프로그래밍입문 (1)
11-1. 예외처리
3 예외처리 (Exception Handling) 예외란? 우리가당연하다고가정한상황이거짓이되는경우 대표적경우 컴퓨터에사용가능한메모리가부족한경우 당연히있을것이라생각한파일이없는경우 사용자가잘못된값을입력하는경우 예외처리란? 이러한상황이발생한경우에도프로그램이올바르게동작할수있도록처리하는것 예 ) 컴퓨터에사용가능한메모리가부족해서동적메모리할당이실패한경우 --> 예외처리를잘하지못한프로그램은비정상종료할수있음반면, 예외처리를잘한프로그램은사용자가작업중이던정보를잘보관한후에정상적으로종료할수있음
4 1. 기존의예외처리방식 int main(void) 예외처리 일반적이지않은프로그램의흐름의처리 에러가아니다! int a, b; cout<<" 두개의숫자입력 : "; cin>>a>>b; cout<<"a/b 의몫 : "<<a/b<<endl; cout<<"a/b 의나머지 : "<<a%b<<endl; return 0; int main(void) int a, b; cout<<" 두개의숫자입력 : "; cin>>a>>b; if(b==0) cout<<" 입력오류!"<<endl; else cout<<"a/b 의몫 : "<<a/b<<endl; cout<<"a/b 의나머지 : "<<a%b<<endl; return 0;
5 2. C++ 의예외처리메커니즘 Try, catch, throw : TCT try /* 예외발생예상지역 */ throw catch( 처리되어야할예외의종류 ) /* 예외를처리하는코드가존재할위치 */ throw ex; // ex 를가리켜보통은그냥 예외 라고표현을한다.
6
7 예제 int main(void) int a, b; cout<<" 두개의숫자입력 : "; cin>>a>>b; try if(b==0) throw b; cout<<"a/b의몫 : "<<a/b<<endl; cout<<"a/b의나머지 : "<<a%b<<endl; catch(int exception) cout<<exception<<" 입력."<<endl; cout<<" 입력오류! 다시실행하세요."<<endl; return 0; 예외가발생하면 try 블록의나머지부분무시 예외처리후 catch 블록이후부터실행
3. Stack Unwinding ( 스택풀기 ) Stack 가장나중에들어온자료가가장먼저나가게되는데이터구조 LIFO(Last-In, First-Out): 후입선출형구조 FILO(First-In,Last-Out): 먼저들어온것이나중에나간다 8
9 Stack 의원리 Push: 스택에새로운자료를추가하는것 Pop: 스택에저장된값을다시빼내어내는것
Stack 풀기 10
11
12 Stack 풀기예제 int divide(int a, int b); // a/b 의몫만반환 int main(void) int a, b; cout<<" 두개의숫자입력 : "; cin>>a>>b; try cout<<"a/b의몫 : "<<divide(a, b)<<endl; catch(int exception) cout<<exception<<" 입력."<<endl; cout<<" 입력오류! 다시실행하세요."<<endl; return 0; int divide(int a, int b) if(b==0) throw b; return a/b;
13
14 예외가처리되지않으면 stdlib.h 에선언되어있는 abort 함수의호출에의해프로그램종료 전달되는예외명시 그이외의예외가전달되면 abort 함수의호출 int fct(double d) throw (int, double, char *).....
15 하나의 try, 여러개의 catch int main(void) int num; cout<<"input number: "; cin>>num; try if(num>0) throw 10; // int형예외전달. else throw 'm'; // char형예외전달. catch(int exp) cout<<"int형예외발생 "<<endl; catch(char exp) cout<<"char형예외발생 "<<endl; return 0;
16 4. 예외상황을나타내는클래스의설계 예외상황을나타내는클래스 & 객체 예외클래스, 예외객체 일반클래스, 객체와다르지않다. 예외상황을알려주기위한용도로사용 예외는클래스의객체로표현되는것이일반적
5. 예외처리에대한나머지문법요소 17 모든예외처리 catch 블록 try..... catch( ) // 선언은모든예외를다처리하겠다는선언..... 예외다시던지기 try..... catch(exception& t)..... throw;
11-2. 템플릿
19 1. 템플릿이란? 템플릿 (template): 물건을만들때사용되는틀이나모형을의미 함수템플릿 (function template): 함수를찍어내기위한형틀
20 함수 get_max() int get_max(int x, int y) if( x > y ) return x; else return y; 만약 float 값중에서최대값을구하는함수가필요하다면? 1. 하나더만든다. 2.
21 함수 get_max() float get_max(float x, float y) if( x > y ) return x; else return y; 핵심적인내용은같고매개변수의타입만달라진다.
22 일반화프로그래밍 일반화프로그래밍 (generic programming) 일반적인코드를작성하고이코드를정수나문자열과같은다양한타입의객체에대하여재사용하는프로그래밍기법
23 get_max() template<typename T> T get_max(t x, T y) if( x > y ) return x; else return y; 자료형이변수처럼표기되어있음을알수있다
템플릿함수의사용 24
예제 25
26 2. 템플릿함수의특수화 template <typename T> void print_array(t[] a, int n) for(int i=0;i<n; i++) cout << a[i] << " "; cout << endl; // 함수템플릿으로정의 template <> // 템플릿특수화 void print_array(char[] a, int n) // 매개변수가 char인경우에는이함수가호출된다. cout << a << endl;
27 함수템플릿과함수중복 중복
함수템플릿과함수중복 28
두개의타입매개변수 29
30 2. 클래스템플릿 클래스템플릿 (class template): 클래스를찍어내는틀 (template) template <typename 타입이름,...> class 클래스이름... 예제 : 하나의값을저장하고있는박스
예제 31
32 실행결과 클래스템플릿으로만들어보자.
클래스템플릿버전 33
34 클래스템플릿정의 template <typename T> class 클래스이름...// T를어디서든지사용할수있다.
예제 35
36
클래스외부에정의 37
38 두개의타입매개변수 두개의데이터를저장하는클래스 Box2
예제 39
예제 40
Q & A 41
42 참고문헌 뇌를자극하는 C++ 프로그래밍, 이현창, 한빛미디어 1 열혈 C++ 프로그래밍 ( 개정판 ), 윤성우, 오렌지미디어 C++ ESPRESSO, 천인국저, 인피니티북스 명품 C++ Programming, 황기태, 생능출판사
추가자료
44 예외객체의사용 객체를예외로던지면다양한정보를함께전달할수있다. class MyException public: const void* sender; const char* description; int info; // 예외를던진객체의주소 // 예외에대한설명 // 부가정보 ; MyException(const void* sender, const char* description, int info) this->sender = sender; this->description = description; this->info = info; // 원소의값을바꾼다 void DynamicArray::SetAt(int index, int value) // 인덱스의범위가맞지않으면예외를던진다 if (index < 0 index >= GetSize()) throw MyException( this, "Out of Range!!!", index); arr[index] = value;
45 다형성을사용해서일관된관리를할수있다. // 인덱스와관련된예외 class OutOfRangeException : public MyException... ; // 메모리와관련된예외 class MemoryException : public MyException... ; try // OutOfRangeException& 혹은 MemoryException& 타입의 // 예외객체를던진다고가정 catch(myexception& ex) // OutOfRangeException 과 MemoryException 모두 // 여기서잡을수있다. cout << " 예외에대한설명 = " << ex.description << "\n";
46 구조적예외처리의규칙 예외는함수를여러개건너서도전달할수있다. int main() try A(); catch(int ex) cout << " 예외 = " << ex << "\n"; void A() void B() void C() return 0; B(); C(); throw 337;
47 받은예외를다시던질수있다. int main() try A(); catch(char c) cout << "main() 함수에서잡은예외 = " << c << "\n"; void A() return 0; try B(); catch(char c) cout << "A() 함수에서잡은예외 = " << c << "\n"; throw; void B() throw 'X';
48 구조적예외처리의규칙 (3) try 블럭하나에여러개의 catch 블럭이이어질수있다. int main() try A(); catch(memoryexception& ex) cout << "MemoryException 타입으로잡혔음 \n"; catch(outofrangeexception& ex) cout << "OutOfRangeException 타입으로잡혔음 \n"; catch(...) cout << " 그밖의타입 \n"; void A() return 0; // throw 100; throw OutOfRangeException(NULL, 0);
49 구조적예외처리의규칙 (4) 실행결과 [24-10] Catch 블록이여럿인경우 [24-11]
50 구조적예외처리의규칙 (5) 예외객체는레퍼런스로받는것이좋다. try MyException e( this, 객체, 0 ); throw e; catch( MyException& ex) cout << ex.description << \n ; 레퍼런스나포인터가아닌객체의타입으로받는경우에는불필요한복사가발생하므로비효율적이다. 객체를동적으로할당해서던지고포인터타입으로받는경우에불필요한복사가발생하지는않지만, 메모리할당과해제를신경써야하므로불편하다.
== 예외처리추가케이스스터디 == 51 1. 예외를나타내는클래스와상속 catch 블록에예외가전달되는방식
52 2. new 연산자에의해전달되는예외 new 연산자가메모리공간할당을실패했을때발생시키는예외 using std::bad_alloc; int main(void) try int i=0; while(1) cout<<i++<<" 번째할당 "<<endl; double(*arr)[10000]=new double[10000][10000]; catch(bad_alloc ex) ex.what(); cout<<endl<<"end"<<endl; return 0;
53 4. 리소스의정리 리소스를정리하기전에예외가발생한경우의문제점확인 try // 메모리를할당한다. char* p = new char [100]; // 여기까지실행되었음을출력한다. cout << " 예외가발생하기전 \n"; // 예외를던진다. throw "Exception!!"; // 이곳은실행되지않음을출력한다. cout << " 예외가발생한후 \n"; // 메모리를해제한다. ( 실행안됨 ) delete[] p; p = NULL; catch(const char* ex) cout << " 예외잡음 : " << ex << "\n";
54 실행결과 [24-12] 실행의흐름 [24-13]
55 5. 소멸자를이용한리소스의정리 예외가발생한경우라도객체의소멸자는반드시호출되므로소멸자를사용해서리소스릭을방지할수있다. // 스마트포인터클래스 class SmartPointer public: SmartPointer(char* p) : ptr(p) ~SmartPointer() // 소멸자가호출되는것을확인한다 cout << " 메모리가해제된다!!\n"; delete[] ptr; public: ; char * const ptr;
56 소멸자를사용해서리소스릭을방지하는예 try // 메모리를할당한다. char* p = new char [100]; // 할당된메모리의주소를스마트포인터에보관한다. SmartPointer sp(p) // 여기까지실행되었음을출력한다. cout << " 예외가발생하기전 \n"; // 예외를던진다. throw "Exception!!"; // 이곳은실행되지않음을출력한다. cout << " 예외가발생한후 \n"; // 메모리를해제해줄필요가없다. // delete[] p; // p = NULL; catch(const char* ex) cout << " 예외잡음 : " << ex << "\n";
57 6. 생성자에서의예외처리 생성자에서예외가발생한경우에는객체가생성되지않은것으로간주되므로소멸자가호출되지않는다. 그러므로, 생성자에서예외가발생한경우에는반드시생성자안쪽에서리소스를정리해야한다. DynamicArray::DynamicArray(int arraysize) try // 동적으로메모리를할당한다. arr = new int [arraysize]; // 배열의길이보관 size = arraysize; // 여기서고의로예외를발생시킨다. throw MemoryException( this, 0); catch(...) cout << " 여기는실행된다!!\n"; delete[] arr; // 리소스를정리한다. throw; // 예외는그대로던진다.
58 7. 소멸자에서의예외처리 소멸자에서예외가발생하는경우에는프로그램이비정상종료할수있으므로, 소멸자밖으로예외가던져지지않게막아야한다. DynamicArray::~DynamicArray() try // 메모리를해제한다. delete[] arr; arr = 0; catch(...)
59 auto_ptr C++ 에서기본적으로제공하는스마트포인터클래스 #include <memory> using namespace std; int main() // 스마트포인터생성 auto_ptr<int> p( new int ); // 스마트포인터의사용 *p = 100; // 메모리를따로해제해줄필요가없다 return 0; [24-17]
60 bad_alloc 동적메모리할당실패시던져지는예외클래스 #include <new> #include <iostream> using namespace std; int main() try // 많은양의메모리를할당한다. char* p = new char [0xfffffff0]; catch(bad_alloc& ex) cout << ex.what() << "\n"; return 0; 실행결과
추가예제 ) 템플릿예제 - 스택 61 스택의연산들 is_empty(s) ::= 스택이비어있는지를검사한다. is_full(s) ::= 스택이가득찼는가를검사한다. push(s, e) ::= 스택의맨위에요소 e를추가한다. pop(s) ::= 스택의맨위에있는요소를삭제한다.
스택의공백상태와포화상태 62
63 isempty(), isfull() isempty() if top = -1 then return TRUE else return FALSE isfull() if top = (MAX_STACK_SIZE-1) then return TRUE else return FALSE
64 push() push(x) if isfull() then error "overflow" else top top+1 stack[top] x
65 pop() pop(x) if isempty() then error "underflow" else e stack[top] top top-1 return e
66 스택의구현 #include <iostream> using namespace std; // 예외처리를위한클래스 class FullStack ; // 예외처리를위한클래스 class EmptyStack ;
template <class T> class Stack private: T* s; int size; int top; public: Stack(int n = 100) : size(n), top(-1) s = new T[size]; ~Stack() delete []s; void push(t v); T pop(); bool isempty() const return top == -1; bool isfull() const return top == size - 1; ; 67
68 스택의구현 template< typename T > void Stack< T >::push( T v ) if ( isfull() ) throw FullStack(); s[ ++top ] = v; template< typename T > T Stack< T >::pop( ) if ( isempty() ) throw EmptyStack(); return s[ top-- ];
계속하려면아무키나누르십시오... 69 int main() Stack<int> s; // 크기가 100 인정수형스택 s.push(100); s.push(200); s.push(300); s.push(400); cout << s.pop() << endl; cout << s.pop() << endl; cout << s.pop() << endl; cout << s.pop() << endl; return 0; 400 300 200 100
스택의응용 70
실행결과 71