profile image

L o a d i n g . . .

반응형

예외처리의 기본

#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;
}

throw -1
throw 'a'

 

 

예외 클래스와 상속

#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++' 강의를 수강하며 개인 백업용으로 메모하였습니다.

인프런: https://www.inflearn.com/course/following-c-plus

 

홍정모의 따라하며 배우는 C++ - 인프런

만약 C++를 쉽게 배울 수 있다면 배우지 않을 이유가 있을까요? 성공한 프로그래머로써의 경력을 꿈꾸지만 지금은 당장 하루하루 마음이 초조할 뿐인 입문자 분들을 돕기 위해 친절하고 자세하게 설명해드리는 강의입니다. 초보로 시작하더라도 중급을 넘어 고급 프로그래머로 가는 길목에 들어서고 싶으시다면 최고의 디딤돌이 되어드리겠습니다. 여러분의 꿈을 응원합니다! 초급 프로그래밍 언어 C++ 온라인 강의 C++

www.inflearn.com

반응형
복사했습니다!