※ 예외처리(Exception Handling): 예외를 처리하는 과정
예외(Exception): 프로그램 실행도중 일어나는 비정상적인 상황
예를 들자면 아래와 같은 함수에서 분모에 0을 넣은 것과 동일한 상황이다.
우측 컴파일부분을 보면 Process finished with exit code -1073741676 (0xC0000094)라 적혀있다.
즉, 정상적이지 않은 값이 나와 오류가 발생했다는 뜻이다.
이런 경우, 프로그램이 오류가 발생했을 때, 컴파일러가 강제로 종료되거나 사전에 방지하기 위해 다음과 같은 방식을 사용한다.
- if문과 같은 조건문을 통한 예외처리
int fun(int a, int b){
if (b == 0)
exit(0);
else
return a / b;
}
int main() {
int x, y;
cin >> x >> y;
cout << fun(x, y) << endl;
}
위와 같이 if문을 통한 예외를 제어할 수도 있다. 이는 C에서도 가능한 방식이다.
하지만 if-else문을 통한 에러처리방식은 에러가 발생한 객체에 대해 수명이 유지되어 에러를 처리하는동안에도 발생한 객체를 참조하는 코드가 정상적으로 컴파일된다.
따라서 C++을 포함한 여러 언어들에서는 다음과 같은 방식을 많이 사용한다.
- try catch를 통한 예외처리
try-catch는 if문의 예외처리와 달리 지역 객체들의 소멸자가 자동호출되어 메모리 누수현상을 조금 해결할 수 있다.
다만 try-catch 블록을 유지해야할 정보도 많고 실제 예외가 발생했을 때, 해줘야 할 일이 많아서 코드 크기나
예외 발생시 처리 속도는 전통적인 if 조건문의 반환 값을 통한 오류처리와는 비교하기 힘들다.
try/catch는 자동으로 해주는 일이 많기에 당연히 더 느리다.
※ try-catch문을 통한 예외처리 매커니즘
class Animal {
public:
Animal() { cout << "생성자" << endl; };
~Animal() { cout << "소멸자" << endl; };
};
int main(){
try {
Animal a;
throw (a);
}
catch(Animal& a){
cout << "catch문" << endl;
}
}
/*출력문*/
//생성자
//소멸자
//catch문
//소멸자
다음과 같이 객체를 throw할 수 있는데, nested block 즉, try{ }가 종료가 되었으므로
Animal a를 통해 만들어진 객체 a는 소멸자에 의해 소멸하게 된다.
Q. 그렇다면 왜 출력문에서 catch문 뒤에 소멸자가 한 번 더 출력된 것일까?
A. 그건 바로 catch(Animal& a)에서 생성자가 한 번 더 생성되었다는 것의 반증이 될 것이다.
생성자는 처음 객체생성되었을 때, 초기화를 위해 딱 한번만 호출된다는 것이 기본 개념이고, 핵심이기 때문이다.
따라서 catch 블록이 종료된 이후 Animal& a를 통해 생성된 객체를 다시 소멸자가 소멸해줘서 소멸자가 두번 출력된 것!
※ Exception 클래스 (예외 클래스)
Exception 클래스: 예외처리를 위해 exception 헤더에서 제공하는 클래스 (표준 라이브러리인 std namespace에 존재)
이런 예외클래스도 클래스이기에 상속은 물론 다형성, 생성자와 연산자에도 적용 가능하다.
§ what()
what은 exception 클래스에서 하나의 문자열 포인터를 반환하는 가상멤버함수이다.
what()은 exception 클래스에서는 아무 의미가 없지만 파생클래스에서 원하는 문자열을 출력할 수 있게 재정의 해준다!
#include <iostream>
#include <exception>
#include <string>
using namespace std;
class NewException : public exception{ //새로운 Exception NewException은 exception클래스를 상속받음
public:
const char* what() const noexcept override{ // what 함수의 오버라이딩 진행
return "NewException";
}
};
int main(){
try{
string str;
str.resize(-100);
}
catch (exception& e){
try {
throw NewException(); // 예외 발생시 새로운 Exception throw
}
catch (const NewException& newException){
cout << "My exception is " << newException.what() << endl; // NewException의 what()에서 전달받은 문자열 출력
}
}
}
§ 표준 Exception 클래스
#include <exception>는 exception 클래스로부터 파생된 다양한 표준 exception 클래스를 정의하고 있다.
[가장 기초클래스가 되는 2개의 클래스, logic과 runtime]
logic_error 클래스는 일반적인 논리에 관한 오류들을 처리
runtime_error 클래스는 프로그램 실행하는 동안 발생할 수 있는 다양한 오류들을 처리
※ Custom Exception (예외클래스의 설계)
예외발생을 알리기 위한 예외 객체의 생성을 위해 정의된 클래스로 기본 자료형 데이터 뿐만 아니라
클래스의 객체도.예외데이터가 될 수 있다. 또한 예외클래스도 상속의 관계를 구성할 수 있다
※ 예외클래스 전달방식의 주의사항
try 뒤를 이어 등장하는 catch가 2개 이상일 때, 다음과 같은 과정을 거친다.
int throwExceptions (int c) {
if (c == 1){
throw string("It's string!");
}
if (c == 2){
throw 2;
}
if (c == 3){
throw "hello world!";
}
if (c == 4){
throw 'X';
}
return 0;
}
int main(){
int c;
while (true) {
cin >> c;
try {
throwExceptions(c);
}
catch (string& s){
cout << "string exception: " << s << endl;
}
catch (const char* s) {
cout << "string literal exception: " << s << endl;
}
catch (char x) {
cout << "char exception: " << x << endl;
}
catch (int x) {
cout << "int exception: " << x << endl;
}
}
}
/*---------------출력문---------------*/
//1
//string exception: It's string!
//2
//int exception: 2
//3
//string literal exception: hello world!
//4
//char exception: X
※ noexcept (true) (feat. move constructor)
예외가 생성되지 않음을 뜻하는 키워드
호출자가 noexcept 여부에 의존할 수 있으며 예외를 방출하지 않을 함수는 noexcept로 선언한다.
[장점]
1. user와 compiler에게 힌트가 됨.
2. code의 단순화
3. compiler의 최적화를 가능하게 함.
int funcA() noexcept {
throw std::runtime_error("Exception!");
return 0;
}
int main(){
try {
funcA();
}
catch (char c){
cout << "catch char: " << c;
}
}
※ 이동생성자와 noexcept
[이동생성자 사용 시 주의할 점]
1. 이동생성자에는 noexcept 키워드가 지정되어야 한다. (이동생성자 수행중, 예외가 없다는 것을 컴파일러가 인지해야함)
2. shallow copy가 일어나는 변수(포인터, 주소관련 변수)에 nullptr를 넣어줘야 함
3, 소멸자에서 메모리할당 관련 변수가 nullptr인지 확인해 줘야함.
Animal(Animal && a) noexcept { //이동생성자에 의한 얕은복사
age = a.age;
name = a.name;
cout << "이동생성자" << endl;
a.name = nullptr;
}
※ rethrowing
- throw를 다시 하여 예외를 부분적으로 다루기(handling) 위해서 사용.
int funcA() {
throw std::runtime_error("Exception!");
return 0;
}
int funcB() {
try {
funcA();
}
catch (std::runtime_error& e) {
cout << "catch Exception: " << e.what() << endl;
throw;
}
}
int main(){
try {
funcB();
}
catch (std::runtime_error& e) {
cout << "catch Rethrowed exception: " << e.what() << endl;
}
}
/*---------------출력문---------------*/
//catch Exception: Exception!
//catch Rethrowed exception: Exception!
'C | C++ > C++' 카테고리의 다른 글
this.code(13)_ 파일처리와 stream (feat. TermProject) (0) | 2022.11.08 |
---|---|
this.code(12)_ auto 키워드와 decltype & 범위기반 for문 (★★★★) (0) | 2022.10.29 |
this.code(10)_ template (★★★★★)과 함수객체(functor) (0) | 2022.10.29 |
this.code(9)_ smart pointer & type casting_ part 2. (0) | 2022.10.29 |
★this.code(8)_ type casting_ part 1. cast operator (★★★) (0) | 2022.10.29 |