언어/C++

[Effective C++] 예외가 소멸자를 떠나지 못하도록 붙들어 놓자

2020. 1. 8. 17:18
반응형

항목 8 : 예외가 소멸자를 떠나지 못하도록 붙들어 놓자

 

소멸자로부터 예외가 터져 나가는 경우를 C++ 언어에서 막는 것은 아니지만, 실제 상황을 들춰보면 확실히 우리가 막을 수 밖에 없는 것 같습니다. 아래의 예를 봅시다.

 
1
2
3
4
5
6
7
8
class DBConnection {
public :
    ...
    static DBConnection create();            // DBConnection 객체를 반환하는 함수. 매개변수는 편의상 생략.
 
 
    void close();                            // 연결을 닫습니다. 이때 연결이 실패하면 예외를 던집니다.
};
Colored by Color Scripter
 

보다시피 사용자가 DBConnection 객체에 대해 close를 직접 호출해야 하는 설계입니다.

자원 관리 클래스의 소멸자가 어떤 형태인지 보겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
class DBConn {                        // DBConnection 객체를 관리하는 클래스
public :
    ...
    ~DBConn();                        // 데이터베이스 연결이 항상 닫히도록 확실히 챙겨주는 함수
    {
        db.close();
    }
 
private :
    DBConnection db;
};
Colored by Color Scripter
 

이렇게 코드를 작성할 시 다음과 같은 프로그래밍이 가능해집니다.

 

1
2
3
4
5
6
7
8
9
10
{                                                                // 블록 시작
   DBConn dbc(DBConnection :: create());           // DBConnection 객체를 생성하고 이것을 DBConn 객체로 넘겨서 관리를 맡깁니다.
 
 
 
    ...                                                            // DBConn 인터페이스를 통해 그 DBConnection 객체를 사용합니다.
 
 
}                                                                // 블록 끝. DBConn 객체가 여기서 소멸됩니다, 따라서 DBConnection 객체에 대한
                                                                // close 함수의 호출이 자동으로 이루어집니다.
Colored by Color Scripter
 

 

close 호출만 정상적으로 동작하면 문제가 될 것이 없는 코드입니다. 그러나 close호출 시 예외가 발생했다고 가정하면 어떻게 될까요?

DBConn의 소멸자는 분명히 이 예외를 전파할 것입니다. 이것이 문제입니다. 예외를 던지는 소멸자는 곧 '걱정거리'를 의미하기 때문입니다.

 

걱정거리를 피하는 방법은 두 가지 입니다.

 

첫 번째, close에서 예외가 발생하면 프로그램을 바로 끝냅니다. 대개 abort를 호출합니다.

 

1
2
3
4
5
6
7
8
DBConn :: ~DBConn()
{
    try { db.close(); }
    catch ( ... ){
        close 호출이 실패했다는 로그를 작성;
        std :: abort();
    }
};
 

 

두 번째, close를 호출한 곳에서 일어난 예외를 삼켜 버립니다.

 

1
2
3
4
5
6
7
8
DBConn :: ~DBConn()
{
    try { db.close(); }
    catch ( ... ){
        close 호출이 실패했다는 로그를 작성;
        std :: abort();
    }
};
 

 

대부분의 경우에서 예외 삼키기는 그리 좋은 발상이 아닙니다. 중요한 정보가 묻혀 버리기 때문입니다.

하지만 때에 따라서는 불완전한 프로그램 종료 혹은 미정의 동작으로 인해 입는 위험을 감수하는 것보다 그냥 예외를 먹어버리는 게 나을 수도 있습니다.

단, 발생한 예외를 그냥 무시한 뒤라도 프로그램이 신뢰성 있게 실행을 지속할 수 있어야 합니다.

 

이것보다 더 좋은 방법을 생각해 볼 수 있을것 같습니다.

인터페이스를 잘 설계해서, 발생할 소지가 있는 문제에 대처할 기회를 사용자가 가질 수 있도록 하면 어떨까요?

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class DBConn {
public :
    ...
    void close()                                        // 사용자 호출을 배려하여 새로 만든 함수
    {
        db.close();
        closed = true;
    }
 
    ~DBConn()
    {
        if(!closed)
        try{                                            // 사용자가 연결은 안 닫았으면
            db.close();                                    // 여기서 닫아 봅니다.
        }
        catch( ... ){                                    // 연결을 닫다가 실패하면,
            close 호출이 실패했다는 로그를 작성합니다;        // 실패를 알린 후에
            ...                                            // 실행을 끝내거나 예외를 삼킵니다.
        }
    }
 
private :
    DBConnection db;
    bool closed;
};
Colored by Color Scripter
 

close 호출의 책임을 DBConn의 소멸자에서 DBConn의 사용자로 떠넘기는 이런 아이디어는 무책임한 책임 전가로 보일 수도 있습니다. 여기서 우리가 알아야 할 것은,

어떤 동작이 예외를 일으키면서 실패할 가능성이 있고 또 그 예외를 처리해야 할 필요가 있다면, 그 예외는 소멸자가 아닌 다른 함수에서 비롯된 것이어야 한다 라는 점 입니다.

 

꼭 잊지 말아야 할 것!

1. 소멸자에서는 예외가 빠져나가면 안 됩니다. 만약 소멸자 안에서 호출된 함수가 예외를 던질 가능성이 있다면, 어떤 예외이든지 소멸자에서 모두 받아낸 후에 삼켜 버리든지 프로그램을 끝내든지 해야 합니다.

2. 어떤 클래스의 연산이 진행되다가 던진 예외에 대해 사용자가 반응해야 할 필요가 있다면, 해당 연산을 제공하는 함수는 반드시 보통의 함수(즉, 소멸자가 아닌 함수)이어야 합니다.

반응형

'언어 > C++' 카테고리의 다른 글

[Effective C++] 대입 연산자는 *this의 참조자를 반환하게 하자  (0) 2020.01.10
[Effective C++] 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자  (0) 2020.01.09
[Effective C++] 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자  (0) 2020.01.07
[Effective C++] 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자  (0) 2020.01.06
[Effective C++] C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자  (0) 2020.01.05
'언어/C++' 카테고리의 다른 글
  • [Effective C++] 대입 연산자는 *this의 참조자를 반환하게 하자
  • [Effective C++] 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자
  • [Effective C++] 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자
  • [Effective C++] 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자
지나가던 개발자
지나가던 개발자
지나가던 개발자
나의 발전을 위한 공간
지나가던 개발자
전체
오늘
어제
  • 분류 전체보기 (221)
    • 언어 (86)
      • C++ (43)
      • JAVA (43)
    • 게임 개발 (4)
      • 간단한 RPG 게임 만들기 (4)
      • 게임 개발 이슈 해결 (0)
    • 백준 문제풀이 (36)
      • PLATINUM (0)
      • GOLD (13)
      • SILVER (21)
      • BRONZE (2)
    • 프로그래머스 문제풀이 (32)
      • LEVEL 5 (0)
      • LEVEL 4 (0)
      • LEVEL 3 (7)
      • LEVEL 2 (19)
      • LEVEL 1 (6)
    • SQL 문제풀이 (15)
      • 프로그래머스 (4)
      • 해커랭크 (11)
    • 디자인 패턴 (1)
    • 웹 (17)
      • 웹 이론 정리 (17)
    • CS 지식 (28)
      • 알고리즘 (0)
      • 데이터베이스 (11)
      • 자료구조 (0)
      • 네트워크 (7)
      • 그래픽스 (0)
      • 운영체제 (9)
      • 기타 (1)
    • Git (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록
  • 깃 허브

공지사항

인기 글

태그

  • Silver 3
  • Chapter 6
  • Chapter 4
  • java
  • 객체 지향 설계
  • 상속
  • 열거 타입과 애너테이션
  • 클래스와 인터페이스
  • Level 2
  • 프로그래머스
  • 설계 및 선언
  • 객체 생성과 파괴
  • level 1
  • 해커랭크
  • Gold 5
  • 백준
  • c++
  • BOJ
  • mysql
  • 소멸자 및 대입 연산자

최근 댓글

최근 글

hELLO · Designed By 정상우.
지나가던 개발자
[Effective C++] 예외가 소멸자를 떠나지 못하도록 붙들어 놓자
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.