C++ 예외처리 버그 발생

익명 사용자의 이미지

(파일 분리한 게 많아서 일단 관련된 파일들만 요약해서 올리겠습니다.)

1. 일단 예외 클래스 구조는 다음과 같습니다.

Exception 클래스를 기초 클래스로 하고 WithdrawException 클래스와 InvalidException 클래스를 유도 클래스로 선언했습니다.

*WithdrawException 클래스 : 예금된 금액보다 더 많이 출금액을 설정한 경우
*InvalidException 클래스 : 입금, 출금 상황에서 마이너스를 금액으로 설정한 경우

2. model이 되는 클래스는 다음과 같습니다.

Member 클래스(어떤 이자율도 갖지 않는 기본 클래스를 말합니다.) 이를 상속하는 NormalMember 클래스(이자율을 설정할 수 있습니다.) 또 이것을 한번 더 상속한 HighCreditMember 클래스(신용등급을 설정해서 기본 이자율과 합산합니다.) 정리하면 3단으로 되어있는 형태입니다. 예외가 발생하면 예외를 핸들러 클래스에게 던집니다.

3. service가 되는 클래스는 BankHandler 클래스로 위에 말씀드린 model클래스를 사용해서 간단한 입출금 서비스를 제공합니다. 서비스를 제공하는 클래스에서 예외를 처리합니다.

====> 버그 발생 : model의 기초 클래스인 Member클래스를 추상 클래스로 선언한 게 아니기 때문에 view클래스에 계좌 생성 메뉴로 3번에 제공됩니다. 이때 Member클래스로 계좌를 생성한 경우, 위에 정의한 두 개의 예외가 발생했을 때 서비스 클래스에서 그 예외를 잡지 못합니다.

그런데 NormalMember나 HighCreditMember로 계좌를 생성하고 예외를 발생시키면 이건 또 예외를 잡아주네요. (Member클래스에서 예외가 발생할 수 있는 부분의 메소드를 재정의했습니다.)

4. code

(1) BankHandler.cpp 내용 중

bool accountHandler::accountInput()
{
String accountNum;

cout << "bank account sign number : ";
cin >> accountNum;

for(int i=0; i {
if(list[i]->get_account_sign_number() == accountNum)
{
while(1) {
//**************************************예외 처리문을 설정해놓았음********
try {
int money = 0;

cout << "input money : ";
cin >> money;

list[i]->input(money);
return true;

} catch(Exception e) {
//그러나 Member.cpp에서 throw한 예외를 잡지 못함
cout << e.getErrorMessage() << endl;
}
//**************************************예외 처리문을 설정해놓았음********
}
}
}

cout << "message : didn't match the member to signal number" << endl;
return false;
}

bool accountHandler::accountOutput()
{
String accountNum;

cout << "bank account sign number : ";
cin >> accountNum;

for(int i=0; i {
if(list[i]->get_account_sign_number() == accountNum)
{
while(1) {
//**************************************예외 처리문을 설정해놓았음********
try {
int money = 0;

cout << "output money : ";
cin >> money;

list[i]->output(money);

return true;

} catch(Exception e) {
//그러나 Member.cpp에서 throw한 예외를 잡지 못함
cout << e.getErrorMessage() << endl;
}
//**************************************예외 처리문을 설정해놓았음********

}
}
}

cout << "message : didn't match the member to signal number" << endl;
return false;
}

(2) Member.cpp 내용 중

void Member::input(int money) throw(Exception)
{
if(money < 0)
{
cout << "execption-invalidAragument occur" << endl;
throw new InvalidArgumentException();
/*분명히 예외를 던짐. 실행 오류를 살펴보면 terminate함수가 호출되어 program 종료되는데, 즉 에러를 핸들러 클래스가 잡지 못한다는 걸 알 수 있음*/
}

account += money;
}

void Member::output(int money) throw(Exception)
{
if(money > account)
{
cout << "exception-withdraw occur" << endl;
/*분명히 예외를 던짐. 실행 오류를 살펴보면 terminate함수가 호출되어 program 종료되는데, 즉 에러를 핸들러 클래스가 잡지 못한다는 걸 알 수 있음*/
throw new WithdrawException();
}
else if(money < 0)
{
cout << "exception-invalidArgument occur" << endl;
/*분명히 예외를 던짐. 실행 오류를 살펴보면 terminate함수가 호출되어 program 종료되는데, 즉 에러를 핸들러 클래스가 잡지 못한다는 걸 알 수 있음*/
throw new InvalidArgumentException();
}

account -= money;
}

File attachments: 
첨부파일 크기
Package icon BankAccount.zip2.03 KB
 의 이미지

혹시 자바 같은 언어 쓰다가 오셨나요?

new WithdrawException()의 타입은 WithdrawException가 아닙니다. WithdrawException *이죠.

즉 질문자님께서는 객체를 던진 것이 아니라 그 포인터를 예외로 던진 겁니다.
그런데 예외를 받는 쪽에서는 Exception e를 잡고 있으니 문제가 되는 것이지요.

WithdrawExceptionException의 파생 클래스라고 해도, WithdrawException *Exception e로 변환할 수 있는 것은 아닐 텐데요.
사실 그런 의미에서 NormalMember나 HighCreditMember에서는 문제없이 돌아간다는 게 저한테는 더 의문입니다. 그 부분 코드는 안 주셨으니 제가 알 수가 없네요.

게다가 예외를 던질 때 힙에서 할당해서 던지는 건 일반적으로 별로 좋은 생각도 아닙니다.

C++에서 예외를 던지고 받을 때 일반적으로 쓰이는 방식은 값으로 던지고, 참조자로 받는 것입니다.

즉, 던질 때는 throw InvalidArgumentException();와 같이 던지고,
받을 때는 catch(Exception &e)와 같이 받는 것이지요.

이에 대한 Rationale은 Scott Meyers의 『More Effective C++』 Item 13: Catch exceptions by reference.를 참조하세요.

이왕 그 책 참조하시는 김에 Item 14: Use exception specifications judiciously.와 Item 15: Understand the costs of exception handling.도 같이 읽어 보시면 좋겠군요.

익명 사용자의 이미지

늦게 답변 드려서 죄송합니다. 네 제가 정말 큰 실수를 범했더라고요 하하..지적해주신 부분이 문제가 된 것 맞습니다. 제가 자바 예외처리와 헷갈렸습니다.
그 하위 클래스 NormalMember나 HighCreditMember에서는 예외를 던질 때 new를 붙이지 않았더군요..
어쨌거나 답변 정말 감사드려요!

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.