반응형
예외처리의 기본
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int findFirstChar(const char * string, char ch)
{
for (std::size_t index = 0; index < strlen(string); ++index)
if (string[index] == ch)
return index;
return -1; // no match
}
// 리턴값을 bool로 처리할 수도 있음
double divide(int x, int y, bool &success)
{
if (y == 0)
{
success = false;
return 0.0;
}
success = false;
return static_cast<double>(x) / y;
}
int main()
{
// 1. 퍼포먼스
// 2. 대체할 수 있는 문법이 없었음
// throw try catch는 약간 느려지는 경우가 있음!
// => 정말 예측할 수 없는 일이 자주 일어날 경우(서버)
bool success;
double result = divide(5, 3, success);
if (!success)
std::cerr << "An error occurred" << std::endl;
else
std::cout << "Result is " << result << std::endl;
std::ifstream input_file("temp.txt");
if (!input_file)
std::cerr << "Cannot open file" << std::endl;
return 0;
}
#include <iostream>
#include <string>
using namespace std;
int main()
{
// try, catch, throw
// try: 시도함
// throw: 시도를 했더니 무슨 일이 일어나서 던짐
// catch: 던져진 일을 받아서 처리함
double x;
cin >> x;
try
{
if (x < 0.0) throw std::string("Negative input");
//if (x < 0.0) throw "Negative input"; // error! 예외처리 시 엄격한 casting!
cout << std::sqrt(x) << endl; // sqrt(): x가 0보다 작을 경우에는 구할 수 없음
}
catch (std::string error_message)
{
//do something to respond
cout << error_message << endl;
}
return 0;
}
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
try
{
// something happened
//throw - 1;
//throw - 1.0; // catch(double) 없으면 error!
throw "My error message";
throw std::string("My error message");
// => 맞는 타입이 없다면 casting을 진행하는 것이 아니라 runtime error 처리
}
catch (int x)
{
cout << "Catch integer " << x << endl;
}
catch (double x)
{
cout << "Catch double " << x << endl;
}
catch (const char * error_message)
{
cout << "Char * " << error_message << endl;
}
catch (std::string error_message)
{
//do something to respond
cout << error_message << endl;
}
return 0;
}
예외처리와 스택 되감기
가장 안쪽에 있는 함수가 예외를 던진다면 스택을 되감아 가며 어디서 예외를 받을지 찾게 된다.
#include <iostream>
using namespace std;
//void last() throw(int) exception specifier
//void last() throw(...) exception specifier
//타입을 뭘 넣든 예외를 던질 가능성이 있는 함수임을 명시함
//주의: parameter가 없으면(throw()) 예외를 안 던질 것이라고 하는 것임!!
void last() //throw(int)
{
cout << "last" << endl;
cout << "Throws exception" << endl;
//throw - 1;
throw 'a'; // 어디서도 못 잡아 주면 runtime error!
// 던지면 출력하지 않고 바로 catch하러 감
cout << "End last" << endl;
}
void third()
{
cout << "Third" << endl;
last();
// catch 구문이 없으므로 거슬러 올라감
cout << "End third" << endl;
}
void second()
{
cout << "Second" << endl;
try
{
third();
}
catch (double)
{
// catch문이 있으나 catch가 double이기 때문에 다시 거슬러 올라감
cerr << "Second caught double exception" << endl;
}
cout << "End second" << endl;
}
void first()
{
cout << "First" << endl;
try
{
second();
}
catch (int)
{
// catch (int)가 있음!
cerr << "First caught int exception" << endl;
}
cout << "End first" << endl;
}
int main()
{
cout << "Start" << endl;
try
{
first();
}
catch (int)
{
// std::cout: 출력을 버퍼에 담아 놓고 바로바로 안 하는 경우가 있음, endl이나 flush 기능 필요
// std::cerr: 에러, 긴급하기 때문에 endl 없어도 됨
// std::clog: 기록
// 기능상의 큰 차이는 없고, 콘솔 화면에 출력하는 세 가지 채널이 있다고 보면 됨
cerr << "main caught int exception" << endl;
}
catch (...) // catch-all handlers
{
cerr << "main caught ellipses exception" << endl;
}
cout << "End main" << endl;
return 0;
}
예외 클래스와 상속
#include <iostream>
using namespace std;
class Exception
{
public:
void report()
{
cerr << "Exception report" << endl;
}
};
class ArrayException : public Exception
{
public:
void report()
{
cerr << "Array exception" << endl;
}
};
class MyArray
{
private:
int m_data[5];
public:
int & operator [] (const int & index)
{
//if (index < 0 || index >= 5) throw - 1;
//if (index < 0 || index >= 5) throw Exception();
if (index < 0 || index >= 5) throw ArrayException();
return m_data[index];
}
};
void doSomething()
{
MyArray my_array;
try
{
my_array[100];
}
catch (const int & x)
{
cerr << "Exception " << x << endl;
}
// Exception부터 catch문 작성 시,
//'ArrayException &': is caught by base class ('Exception &') on line 53
// 부모 클래스에 걸리기 때문에 ArrayException에는 잡히지 못할 것임! 순서 유의
catch (ArrayException & e)
{
cout << "doSomething()" << endl;
e.report();
throw e; // rethrow
}
// ArrayException을 Exception으로 받을 수는 있으나
// 객체 잘림으로 인해 Exception의 report()가 실행됨!
catch (Exception & e)
{
e.report();
//throw e; // ArrayException을 Exception으로 받았을 때, 객체 잘림 현상이 일어남
// 이 상태에서 e를 throw해 버리면 main 함수에서도 ArrayException이 아니라 Exception으로 받음
throw; // 이런 경우 그냥 throw해 주면, 맨 처음 들어온 ArrayException을 던지게 됨
}
}
int main()
{
try
{
doSomething();
}
// 이미 doSomething()에서 해당 예외에 대한 예외 처리를 했을 경우,
// main 함수의 catch문에서는 따로 처리하지 않음
// 다시 throw해 주면 여기서도 처리가 가능함!
catch (ArrayException & e)
{
cout << "main()" << endl;
e.report();
}
return 0;
}
std::exception 소개
#include <iostream>
#include <exception>
#include <string>
using namespace std;
int main()
{
try
{
string s;
s.resize(-1); // 내부에서 exception을 throw하게끔 되어 있음
}
catch (length_error & exception) // 자식 클래스 중 하나
{
cerr << "Length_Error" << endl;
cerr << exception.what() << endl;
}
catch (exception & exception)
{
cout << typeid(exception).name() << endl; // 어떤 exception인지
cerr << exception.what() << endl;
}
return 0;
}
#include <iostream>
#include <exception>
#include <string>
using namespace std;
class CustomException : public exception
{
public:
// noexcept: C++ 11 이상에서 붙여 줘야 함
const char * what() const noexcept override
{
return "Custom exception";
}
};
int main()
{
try
{
//throw std::runtime_error("Bad thing happend");
throw CustomException();
}
catch (exception & exception)
{
cout << typeid(exception).name() << endl; // 어떤 exception인지
cerr << exception.what() << endl;
}
return 0;
}
함수 try
#include <iostream>
using namespace std;
class A
{
private:
int m_x;
public:
A(int x) : m_x(x)
{
if (x <= 0)
throw 1;
}
};
class B : public A
{
public:
/*
B(int x)
: A(x)
{}*/
B(int x) try : A(x)
{
// do initialization
}
catch (...)
{
cout << "Catch in B constructor" << endl;
//throw; //생성자에서 function try를 사용할 경우, throw가 없어도 main의 catch에서 한 번 더 잡음!
}
};
// function try
/*
void doSomething()
try
{
throw - 1;
}
catch (...)
{
cout << "Catch in doSomething()" << endl;
}*/
int main()
{
try
{
//doSomething();
B b(0);
}
catch (...)
{
cout << "Catch in main()" << endl;
}
return 0;
}
예외처리의 위험성과 단점
#include <iostream>
#include <memory>
using namespace std;
int main()
{
try
{
int *i = new int[1000000];
unique_ptr<int> up_i(i); // unique 포인터가 알아서 지워 줌
// do something with i
throw "error";
// 예외가 발생한다면 delete가 실행되지 못함!! memory leak
//delete[] i;
}
catch (...)
{
cout << "Catch" << endl;
}
return 0;
}
#include <iostream>
#include <memory>
using namespace std;
class A
{
public:
//'A::~A': function assumed not to throw an exception but does
//destructor or deallocator has a (possibly implicit) non-throwing exception specification
// 소멸자에서는 예외를 못 던짐!! 런타임 에러
~A()
{
throw "error";
}
};
int main()
{
// 예외처리는 추가적인 연산이 필요하기 때문에 느림 => 반복문 안에 넣으면 속도가 아주 느려질 것임
// 모든 오류를 전부 예외처리로 잡으려고 하지 말기, 정상적으로 작동하는 건 if문 등으로 정상적으로 작동하게끔!
// 네트워크, 분산 처리, 병렬 처리, 하드웨어 관련... 예측할 수 없는 경우에만 사용하기
try
{
A a;
}
catch (...)
{
cout << "Catch" << endl;
}
return 0;
}
해당 포스트는 '홍정모의 따라하며 배우는 C++' 강의를 수강하며 개인 백업용으로 메모하였습니다.
반응형