CPP Module 05

개발새발·2022년 6월 24일
0

42Cursus

목록 보기
19/29
post-thumbnail
post-custom-banner

CPP Module 05

해당 과제를 진행하는데 있어 필요한 사전 지식들을 정리하였습니다.

1. C++의 예외처리

예외(exception)란 프로그램의 실행 도중에 발생하는 문제상황을 의미한다. 문법적인 오류가 아닌, 프로그램의 논리에 맞지 않는 상황인 것이다.

if문을 통해 간단하게 예외를 처리할 수도 있지만 그렇게 되면 예외처리를 위한 코드와 프로그램의 흐름을 구성하는 코드를 쉽게 구분하지 못한다는 단점이 있다. 따라서 C++은 구조적으로 예외를 처리할 수 있는 메커니즘을 제공한다. 이를 이용하면 예외처리 부분을 프로그램의 일반적인 흐름에서 독립시킬 수 있기 때문에 코드의 가독성과 유지보수성을 높일 수 있다. 이 메커니즘은 다음의 3가지 키워드를 통해 이용할 수 있다.

try

  • 예외발생에 대한 검사의 범위를 지정한다.

catch

  • try 블록의 뒤에 바로 등장하며, try 블록에서 발생한 예외를 처리하는 코드가 담기는 영역이다.
  • 형태가 반환형 없는 함수와 유사하다.
  • 하나의 try 블록에 이어서 다수의 catch 블록이 존재할 수 있다. (예외 데이터의 자료형이 상황에 따라 다를 수 있기 때문)

throw

  • 예외가 발생했음을 알린다.
  • 예외상황에 대한 정보를 담은, 의미 있는 데이터를 catch문의 매개변수로 전달한다.
  • 어떤 함수 안에서 예외가 발생하여 throw절이 실행됐는데 그 함수 내에 try~catch문이 존재하지 않는다면 예외처리에 대한 책임은 그 함수를 호출한 영역으로 넘어가게 된다. (스택 풀기(stack unwinding)의 개념)

예외의 발생과 처리를 코드로 정리하면 다음과 같다.

try
{
	...
    if (예외가 발생한다면)
    	throw expn;
    ...
}
catch (type exn)
{
	// 예외처리 코드
}

정리하자면, throw에 의해 던저진 예외 데이터는 예외 데이터를 감싸는 try 블록에 의해서 감지가 되어 이어서 등장하는 catch 블록에 의해 처리되는 것이다.

중요한 점은 예외가 발생하면(throw절이 실행되면), 프로그램의 흐름이 중지되고, catch 블록에 의해서 예외의 처리과정을 거치게 되는데, catch 블록 실행 후, 예외가 발생한 지점 이후를 실행하는 것이 아니라, catch 블록 이후가 실행된다는 점이다.

예외 클래스를 정의해서 예외 객체를 생성할 수도 있다. 예외 객체를 이용해서 예외 상황을 알리면, 예외가 발생한 원인에 대한 정보를 보다 자세히 담을 수 있다. 다만 클래스를 정의할 때, 너무 복잡하지 않게, 예외 표현을 위한 최소한의 기능만 담는 것이 좋다. 예외 클래스도 상속의 관계를 구성하는 것이 가능하다.

 

2. ex00 (Mommy, when I grow up, I want to be a bureaucrat!)

예외 상황에 대한 처리를 사용자가 정의하고 싶다면, std::exception 클래스를 상속하는 예외 클래스를 다음과 같이 정의하면 된다.

class CustomException : public std::exception
{
public :
	const char* what(void) const throw();
    {
    	return ("Error Message");
    }
};

위의 what() 멤버 함수는 std::exception 클래스에 virtural로 정의된 멤버 함수를 overrring한 것이다. 유의할 점은 반환형이 const char * 이기 때문에 예제와 같이 반환값을 리터럴 값 등으로 줘야한다는 점과 끝에 명시된 throw() 키워드는 컴파일 타임에 이용되는 키워드로, 런 타임에 이용되는 throw 절과 혼동하면 안된다는 점이다. throw() 키워드는 하나의 고유한 구문이다. 위의 what() 멤버 함수는 정의된 예외 상황에 대한 문자열을 반환한다.

해당 과제의 요구사항은 Bureaucrat 클래스를 정의할 때, 멤버 변수 grade에 할당될 값이 1 ~ 150의 범위를 벗어나게 되면 객체 생성 시 예외를 발생시키는 것이다. 위의 예외 클래스 예제 형식에 맞춰 Bureaucrat 클래스 안에 std::exception 클래스를 상속하는 GradeTooHighException 클래스와 GradeTooLowException 클래스를 작성하면 된다. 그리고 incrementGrade 함수와 decrementGrade 함수를 호출했을 때, 멤버 변수 grade의 값이 범위를 벗어나게 되는 상황에서도 정의한 예외를 적절히 발생시켜주면 된다.

 

3. ex01 (Form up, maggots!)

ex00Bureaucrat 클래스처럼, Form 클래스 또한 범위를 벗어난 grade 값이 주어지는 경우 생성자에서 적절한 exception을 던지도록 구현해야 한다.

Form 클래스의 멤버 변수들은 대부분 const로 정의되어 있어 operator=overloading할 때 타입 캐스트 연산자 const_cast<T>를 사용하여야 한다.

Bureaucrat 클래스의 signForm 멤버 함수를 정의할 때는, 함수 내에서 try-catch 구문을 만들고, 그 안에서 인자로 주어진 Form 객체를 이용하여 beSigned 함수를 호출하도록 만든다. 그리고 beSigned 함수 내에서는 Bureaucrat 객체의 등급이 충분히 높지 않아 Form에 sign하지 못하는 경우, exception을 던지도록 구현한다. 이렇게 BureaucratsignForm 함수와 FormbeSigned 함수를 엮으면 main 함수에서 일일이 두 함수를 각각의 try~catch 문으로 묶어 호출하는 번거로운 일을 방지할 수 있다.

 

4. ex02 (No, you need form 28B, not 28C...)

기존의 Form 클래스에 순수 가상 함수를 정의하여 추상 클래스로 바꿔준다. 그리고 Form 클래스를 상속하는 ShrubberyCreationForm 클래스와, RobotomyRequestForm 클래스와, PresidentialPardonForm 클래스를 정의하고, 순수 가상 함수인 Form 클래스의 excute 함수를 각 사용법에 맞게 overriding한다.

ex01signForm 멤버 함수를 정의했을 때와 같이 Form 클래스의 유도 클래스들의 execute 멤버 함수는 Bureaucrat 클래스의 executeForm 멤버 함수를 통해 호출하게 만들고 이를 try~catch 문으로 묶어준다. execute 함수에서 발생한 예외는 Form 클래스에 정의해 둔 exception을 던져서 처리되게 한다. 이렇게 하면 executeForm 함수와 execute 함수를 따로 호출하지 않아도 되고, 한번의 executeForm 함수 호출로 execute 함수에서 발생한 exception까지 잡아서 결과를 출력해주게 된다.

ShrubberyCreationForm

  • sign 등급 : 145
  • execute 등급 : 137
  • execute 성공 시, target_shrubbery라는 파일을 생성해 아스키트리를 저장

RobotomyRequestForm

  • sign 등급 : 72
  • execute 등급 : 45
  • execute 성공 시, 드릴 소리와 함께 50%의 확률로 로봇화 진행 후 성공 여부 출력

PresidentialPardonForm

  • sign 등급 : 25
  • execute 등급 : 5
  • execute 성공 시, 사면 되었다는 메시지 출력

 

5. ex03 (At least this beats coffee-making)

ex02에서 Intern 클래스만 추가로 정의해주면 된다. Intern 클래스의 makeForm 멤버 함수는 Form 클래스를 객체화하여 반환해준다. makeForm 함수는 두 개의 string을 매개변수로 받는데 하나는 Form의 이름(target)이고 하나는 ex02에서 만든 3가지 Form 중 어떤 것을 쓸건지에 대한 정보다. 3가지 중 어느 것에도 해당되지 않는다면 에러 메시지를 출력한다.

과제에서는 if/elseif/else 구문을 사용하지 말라고 했으므로 Module 01 때와 같이 멤버 함수 포인터를 이용하면 된다. 그리고 주어진 Form에 일치하는 객체를 생성하여 포인터를 반환하면 된다.

 

참고
https://bigpel66.oopy.io/library/42/inner-circle/16
https://velog.io/@hey-chocopie/cpp-module-05

profile
블록체인 개발 어때요
post-custom-banner

0개의 댓글