Download presentation
Presentation is loading. Please wait.
1
C++ 프로그래밍 26 2008년 2학기 전자정보공학대학 컴퓨터공학부
2
Outline Review: 예외처리 접근 범위와 종속기간 네임 스페이스
3
예외 처리(Exception Handling)
예외란? 우리가 당연하다고 가정한 상황이 거짓이 되는 경우 대표적인 예외 컴퓨터에 사용 가능한 메모리가 부족한 경우 당연히 있을 것이라 생각한 파일이 없는 경우 사용자가 잘못된 값을 입력하는 경우 예외 처리란? 프로그램이 항상 올바르게 동작할 수 있도록 처리하는 것 예) 동적 메모리 할당이 실패한 경우 예외 처리를 잘 하지 못한 프로그램 비정상 종료 예외 처리를 잘 한 프로그램 사용자가 작업 중이던 정보를 잘 보관한 후 정상 종료
4
구조적 예외 처리(1) try와 throw, catch를 사용 try { if(예외발생) throw; 수행할 일(); }
오류 발생 시 할 일;
5
구조적 예외 처리(2) throw, try, catch의 사용 [24-5]
6
구조적 예외 처리(3) C++의 구조적 예외 처리를 사용해서 실패를 알리는 예 // 원소의 값을 바꾼다
void DynamicArray::SetAt(int index, int value) { // 인덱스의 범위가 맞지 않으면 예외를 던진다 if (index < 0 || index >= GetSize()) throw "Out of Range!!!"; arr[index] = value; }
7
구조적 예외 처리(4) C++의 구조적 예외 처리를 사용해서 실패를 알리는 예 // 크기가 10인 배열 객체를 만든다.
DynamicArray arr(10); try { arr.SetAt(5, 100); arr.SetAt(8, 100); arr.SetAt(10, 100); } catch(const char* ex) cout << "예외 종류 : " << ex << "\n";
8
구조적 예외 처리(5) 예외의 발생과 실행의 흐름 [24-6]
9
예외 객체의 사용(1) 객체를 예외로 던지면 다양한 정보를 함께 전달할 수 있다. 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; } };
10
예외 객체의 사용(2) 객체를 예외로 던지면 다양한 정보를 함께 전달할 수 있다. // 원소의 값을 바꾼다
void DynamicArray::SetAt(int index, int value) { // 인덱스의 범위가 맞지 않으면 예외를 던진다 if (index < 0 || index >= GetSize()) throw MyException( this, "Out of Range!!!", index); arr[index] = value; }
11
예외 객체의 사용(2) 다형성을 사용해서 일관된 관리를 할 수 있다. // 인덱스와 관련된 예외
class OutOfRangeException : public MyException { ... }; // 메모리와 관련된 예외 class MemoryException : public MyException
12
예외 객체의 사용(2) 다형성을 사용해서 일관된 관리를 할 수 있다. try {
// OutOfRangeException& 혹은 // MemoryException& 타입의 // 예외 객체를 던진다고 가정 } catch(MyException& ex) // OutOfRangeException 과 // MemoryException 모두 // 여기서 잡을 수 있다. cout << "예외에 대한 설명= " << ex.description << "\n";
13
구조적 예외 처리의 규칙(1) 예외는 함수를 여러 개 건너서도 전달할 수 있다 void A() int main() { {
B(); } void B() C(); void C() throw 337; int main() { try A(); } catch(int ex) cout << "예외 = " << ex; return 0;
14
구조적 예외 처리의 규칙(2) 받은 예외를 다시 던질 수 있다. int main() { try { A();}
catch(char c){ cout << "main() 함수에서 잡은 예외 = " << c << "\n"; } return 0; void A() try { B(); } catch(char c) { cout << "A() 함수에서 잡은 예외 = " << c << "\n"; throw; void B() throw 'X';
15
구조적 예외 처리의 규칙(3) try 블럭 하나에 여러 개의 catch 블럭 int main() { try{ A(); }
catch(MemoryException& ex) cout << "MemoryException 타입으로 잡혔음\n"; } catch(OutOfRangeException& ex) cout << "OutOfRangeException 타입으로 잡혔음\n"; catch(...) cout << "그 밖의 타입\n"; return 0;
16
구조적 예외 처리의 규칙(4) Catch 블록이 여럿인 경우 [24-11]
17
구조적 예외 처리의 규칙(5) 예외 객체는 레퍼런스로 받는 것이 좋다. try {
MyException e( this, “객체”, 0 ); throw e; } catch( MyException& ex) cout << ex.description << “\n”;
18
리소스의 정리(1) 리소스를 정리하기 전에 예외가 발생한 경우의 문제점 확인 try { // 메모리를 할당한다.
char* p = new char [100]; // 여기까지 실행되었음을 출력한다. cout << "예외가 발생하기 전\n"; // 예외를 던진다. throw "Exception!!"; // 이곳은 실행되지 않음을 출력한다. cout << "예외가 발생한 후\n"; // 메모리를 해제한다. (실행 안됨) delete[] p; p = NULL; } catch(const char* ex) cout << "예외 잡음 : " << ex << "\n";
19
리소스의 정리(2) 실행의 흐름 [24-13]
20
소멸자를 사용한 리소스 정리(1) 예외가 발생한 경우라도 객체의 소멸자는 반드시 호출된다 소멸자를 사용 메모리 정리
// 스마트 포인터 클래스 class SmartPointer { public: SmartPointer(char* p) : ptr(p) } ~SmartPointer() // 소멸자가 호출되는 것을 확인한다 cout << "메모리가 해제된다!!\n"; delete[] ptr; char * const ptr; };
21
소멸자를 사용한 리소스 정리(2) 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";
22
생성자에서의 예외 처리 생성자에서 예외가 발생한 경우에는 객체가 생성되지 않은 것으로 간주되므로 소멸자가 호출되지 않는다.
그러므로, 생성자에서 예외가 발생한 경우에는 반드시 생성자 안쪽에서 리소스를 정리해야 한다.
23
생성자에서의 예외 처리 DynamicArray::DynamicArray(int arraySize) { try
// 동적으로 메모리를 할당한다. arr = new int [arraySize]; // 배열의 길이 보관 size = arraySize; // 여기서 고의로 예외를 발생시킨다. throw MemoryException( this, 0); } catch(...) cout << "여기는 실행된다!!\n"; delete[] arr; // 리소스를 정리한다. throw; // 예외는 그대로 던진다.
24
소멸자에서의 예외 처리 소멸자에서 예외가 발생하는 경우에는 프로그램이 비정상 종료할 수 있으므로, 소멸자 밖으로 예외가 던져지지 않게 막아야 한다. DynamicArray::~DynamicArray() { try // 메모리를 해제한다. delete[] arr; arr = 0; } catch(...)
25
auto_ptr C++에서 기본적으로 제공하는 스마트 포인터 #include <memory>
using namespace std; int main() { // 스마트 포인터 생성 auto_ptr<int> p( new int ); // 스마트 포인터의 사용 *p = 100; // 메모리를 따로 해제해 줄 필요가 없다 return 0; } [24-17]
26
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;
27
접근 범위와 존속 기간
28
지역 변수와 전역 변수의 비교(1) 지역 변수와 전역 변수의 접근 범위와 존속 기간 [표 26-1]
29
지역 변수와 전역 변수의 비교(2) 지역 변수와 전역 변수를 정의하고 사용하는 예
int global = 10; // 전역 변수의 정의 void MyFunction(); int main() { int local = 20; // 지역 변수의 정의 global = 100; // 전역 변수에 접근 – 성공 local = 200; // 같은 함수의 지역 변수에 접근 – 성공 MyFunction(); // 함수 호출 return 0; } void MyFunction() local = 200; // 다른 함수의 지역 변수에 접근 – 실패!!
30
extern 다른 소스 파일에 정의된 전역 변수를 사용하는 예 // A.cpp // 전역 변수 int ga = 100;
// Example.cpp // 다른 파일에 있는 전역 변수에 // 접근 하기위한 준비 extern int ga; int main() { // 다른 파일에 있는 전역 변수에 접근 ga = 200; // 성공 return 0; }
31
블록 안에서 정의한 변수(1) 블록 안에서 정의한 변수의 접근 범위와 존속 기간 [26-2]
32
블록 안에서 정의한 변수(2) 블록 안에서 정의한 변수의 접근 범위와 존속 기간 int main() { // 블록을 만든다.
// 블록 안에서 변수 정의 int var_in_block = 10; // 변수에 접근 var_in_block = 100; // 성공 } var_in_block = 1000; // 실패 return 0;
33
다른 영역에 정의한 변수의 이름 중복 각각 다른 영역에 같은 이름의 변수를 정의하는 예 int x = 100;
int main() { cout << "1. x = " << x << "\n"; int x = 200; cout << "2. x = " << x << "\n"; cout << "3. x = " << x << "\n"; int x = 300; cout << "4. x = " << x << "\n"; } cout << "5. x = " << x << "\n"; return 0;
34
다른 영역에 정의한 변수의 이름 중복 실행 결과
35
for 명령 안에서 정의한 변수 for 명령 안에서 정의한 변수의 존속 기간과 접근 범위는 for 명령의 블록 안쪽으로 제한된다. 그러므로, 다음과 같이 for 명령 바깥쪽에서 같은 이름의 변수를 정의하는 것이 가능하다. (표준) 그러나, VC 같이 오래된 컴파일러는 for 명령 바깥쪽에서도 변수를 접근할 수 있도록 구현되어 있기 때문에, 위와 같은 코드는 컴파일 오류를 발생시킨다. VC++.NET 의 경우에는 기본적으로 두 가지 방식 모두를 허용하며, 컴파일 옵션으로 한 가지 방식을 강요할 수도 있다. for (int i = 0; i < 10; i++) { } int i = 100; // 성공
36
static으로 정의한 지역 변수 static인 지역 변수는 프로그램이 종료될 때까지 살아있게 된다. void Sub() {
static int n = 0; cout << "n = " << ++n << "\n"; } int main() Sub(); return 0;
37
static으로 정의한 지역 변수 실행 결과
38
static으로 정의한 전역 변수 static인 전역 변수는 다른 파일에서 접근 할 수 없게 된다. // A.cpp
// 정적 전역 변수 static int ga = 100; // Example.cpp // 다른 파일에 있는 전역 변수에 // 접근 하기위한 준비 extern int ga; int main() { // 다른 파일에 있는 전역 변수에 접근 ga = 200; // 실패 !! return 0; }
39
다른 파일에서 정의한 함수에 접근하기 변수와 마찬가지로 extern 키워드를 사용할 수 있다
// A.cpp void Func() { } // Example.cpp extern void Func(); int main() { Func(); // 성공 return 0; }
40
static 으로 정의한 함수 static으로 정의한 전역 변수와 마찬가지로 다른 파일에서 접근 할 수 없게 된다.
// A.cpp static void Func() { } // Example.cpp extern void Func(); int main() { Func(); // 실패!! return 0; }
41
C 언어로 작성한 함수 사용하기 C언어로 작성한 함수를 사용하는 예 // A.c : C언어로 작성한 파일 (확장자가 .c 다)
void Func() { } // Example.cpp extern “C” void Func(); int main() { Func(); // 성공 return 0; } [표 26-2]
42
C 언어로 작성한 함수 사용하기 같은 원형의 함수이더라도 C언어와 C++언어에서 해석하는 방식에 차이가 있으므로 extern “C”를 붙여주지 않으면 컴파일 오류가 발생한다.
43
네임스페이스
44
네임스페이스 안에서 정의하기(1) 네임스페이스를 사용하기
45
네임스페이스 안에서 정의하기(2) 네임스페이스 안에서 변수, 함수, 구조체, 클래스를 정의하는 예 namespace Dog {
struct Info char name[20]; int age; }; Info dogs[20]; // 멍멍이 리스트 int count; // 전체 멍멍이들의 수 void CreateAll(); // 모든 멍멍이 생성 함수 } namespace Cat class Info public: void Meow(); protected: Info cats[20]; // 야옹이 리스트 int count; // 전체 야옹이 들의 수 void CreateAll(); // 모든 야옹이 생성 함수 // 전체 멍멍이와 야옹이들의 수 int count; 네임스페이스 안에서 변수, 함수, 구조체, 클래스를 정의하는 예
46
네임스페이스 지정하기 네임스페이스를 지정해서 네임스페이스 안에 정의한 이름 사용하기 // 모든 야옹이를 생성한다.
Cat::CreateAll(); // 야옹이 배열에 접근한다. Cat::cats[0].Meow(); // 모든 멍멍이를 생성한다. Dog::CreateAll(); // 멍멍이의 개수를 얻어온다. int dog_count = Dog::count;
47
using 키워드 사용하기 using 키워드로 사용할 네임스페이스 지정하기 using 키워드로 사용할 이름 지정하기
using namespace Cat; // 모든 야옹이를 생성한다. CreateAll(); // 야옹이 배열에 접근한다. cats[0].Meow(); using Cat::CreateAll; // 모든 야옹이를 생성한다. CreateAll();
48
네임스페이스와 소스 파일 하나의 파일에 여러 개의 네임 스페이스가 등장할 수 있다.
여러 파일에 걸쳐서 하나의 네임스페이스가 등장할 수 있다.
49
이름 없는 네임스페이스 이름 없는 네임스페이스 안에 정의된 이름은 다른 소스 파일에서 접근할 수 없다. (static 전역 변수, 함수와 동일한 효과) // Example.h // a.cpp에서 정의된 g를 사용하기 위한 준비 extern int g; int main() { g = 200; // 실패 return 0; } namespace { int g; } void Func() g = 200; // 성공
50
중첩된 네임스페이스 네임스페이스 안에 또 다른 네임스페이스를 만들 수 있다. namespace Data {
namespace User int number; } int main() int user_number = Data::User::number; return 0;
51
네임스페이스를 별칭으로 부르기 네임스페이스에 별칭을 붙여줄 수 있다.
namespace this_namespace_has_a_very_long_name { int n; } // 별명을 붙여준다. namespace oh = this_namespace_has_a_very_long_name; int main() oh::n = 100; return 0;
Similar presentations