언어/C++

[Effective C++] 자원 관리 클래스에서 관리되는 자원은 외부에서 접근할 수 있도록 하자

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

항목 15 : 자원 관리 클래스에서 관리되는 자원은 외부에서 접근할 수 있도록 하자

 

자원 관리 클래스는 재치 만발의 듬직한 클래스입니다. 실수로 터질 수 있는 자원 누출을 튼튼히 막아 주는 보호벽 역할을 해 주니까요.

 

1
std::tr1::shared_ptr<Investment> pInv(createInvestment());
 

 

이때 어떤 Investment 객체를 사용하는 함수로서 여러분이 사용하려고 하는 것이 다음과 같다고 가정해 봅시다.

 

1
int daysHeld(const Investment *pi);            // 투자금이 유입된 이후로 경과한 날수
 

 

그리고 이렇게 호출하고 싶을 텐데요.

 

1
int days = daysHeld(pInv);                // 에러
 

 

애석하게도 이 코드는 컴파일이 안 됩니다. daysHeld 함수는 Investment* 타입의 실제 포인터를 원하는데, 이 코드에서는 tr1::shared_ptr<Investment> 타입의 객체를 넘기기 때문입니다.

 

사정이 이렇다 보니, RAII 클래스의 객체를 그 객체가 감싸고 있는 실제 자원으로 변환할 방법이 필요해집니다. 이런 목적에 일반적인 방법을 쓴다면 두 가지가 있는데, 하나는 명시적 변환이고 또 다른 하나는 암시적 변환입니다.

 

tr1::shared_ptr 및 auto_ptr은 명시적 변환을 수행하는 get이라는 멤버 함수를 제공합니다.

 

1
int days = daysHeld(pInv.get());                // 이제 문제없습니다. pInv에 들어 있는 실제 포인터를 daysHeld에 넘기니까요.
 

 

제대로 만들어진 스마트 포인터 클래스라면 거의 모두가 그렇듯, tr1::shared_ptr과 auto_ptr은 포인터 역참조 연산자(operator-> 및 operator*)도 오버로딩하고 있습니다. 따라서 자신이 관리하는 실제 포인터에 대한 암시적 변환도 쉽게 할 수 있습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Investment {                                                    // 여러 형태의 투자를 모델링한
public :                                                             // 투자 클래스들의 최상위 클래스
    bool isTaxFree() const;
    ...
};
 
Investment* createInvestment();                                        // 팩토리 함수
 
std::tr1::shared_ptr<Investment> pi1(createInvestment());            // tr1::shared_ptr이 자원 관리를 맡도록 합니다.
 
bool taxable1 = !(pi1 -> isTaxFree());                                // operator->을 써서 자원에 접근합니다.
...
std::auto_ptr<Investment> pi2(createInvestment());                    // auto_ptr로 하여금 자원 관리를 맡도록 합니다.
 
bool taxable2 = !((*pi2).isTaxFree());                                // operator*를 써서 자원에 접근합니다.
...
Colored by Color Scripter
 

 

RAII 객체 안에 들어 있는 실제 자원을 얻어낼 필요가 종종 생기기 때문에, RAII 클래스 설계자 중에는 암시적 변환 함수를 제공하여 자원 접근을 매끄럽게 할 수 있도록 만드는 분도 있답니다. 예를 들어, 어떤 하부 수준 C API로 직접 조작이 가능한 폰트를 RAII 클래스로 둘러싸서 쓰는 경우를 생각해 보죠.

 

1
2
3
4
5
6
7
8
9
10
11
12
FontHandle getFont();                                    // C API에서 가져온 함수    - 매개변수가 있으면 복잡하기 때문에 생략했습니다.
 
void releaseFont(FontHandle fh);                        // 역시 같은 C API에서 가져온 함수
 
class Font {                                            // RAII 클래스
public :
    explicit Font(FontHandle fh) : f(fh) { }            // 자원을 획득합니다. 여기서 값에 의한 전달이 수행되는 것에 주의하세요
    ~Font() { releaseFont(f); }                            // 자원 해제를 C API로 하기 떄문입니다.
 
private :
    FontHandle f;                                        // 실제 폰트 
};
Colored by Color Scripter
 

 

Font 객체를 FontHandle로 변환해야 할 경우도 있을 것 이라는 예상을 해 볼 수 있죠. Font 클래스에서는 이를 위한 명시적 변환 함수로 get을 제공할 수 있을 것 입니다.

 

1
2
3
4
5
6
class Font {
public :
    ...
    FontHandle get() const { return f; }                // 명시적 변환 함수
    ...
};
Colored by Color Scripter
 

 

이렇게 해 두면 어쨌든 쓸 수 있긴 한데, 사용자는 하부 수준 API를 쓰고 싶을 때마다 get을 호출해야 할 것입니다.

 

1
2
3
4
5
6
void changeFontSize(FontHandle f, int newSize);            // 폰트 API의 일부
 
Font f(getFont());
int newFontSize();
...
changeFontSize(f.get(), newFontSize);                    // Font에서 FontHandle로 명시적으로 바꾼 후에 넘깁니다.
 

 

변환할 때마다 무슨 함수를 호출해 주어야 한다는 점이 짜증나고 이것 때문에 폰트 자원이 누출될 가능성이 늘어난다면 좋지 않을 것입니다.

 

대안이 없진 않습니다. FontHandle로의 암시적 변환 함수를 Font에서 제공하도록 하면 되는 거죠.

 

1
2
3
4
5
6
class Font {
public :
    ...
    operator FontHandle() const { return f; }                // 암시적 변환 함수
    ...
};
Colored by Color Scripter
 

 

암시적 변환 함수 덕택에 C API를 사용하기가 훨씬 쉬워지고 자연스러워집니다.

 

1
2
3
4
Font f(getFont());
int newFontSize();
...
changeFontSize(f, newFontSize);            // Font에서 FontHandle로 암시적 변환을 수행합니다.
 

 

그렇다고 마냥 좋은 것만은 아닙니다. 암시적 변환이 들어가면 실수를 저지를 여지가 많아집니다.

 

1
2
3
Font f1(getFont());
...
FontHandle f2 = f1;                // 원래 의도는 Font 객체를 복사하는 것이었는데, 엉뚱하게도 f1이 FontHandle로 바뀌고 나서 복사되어 버렸습니다.
 

 

RAII 클래스를 실제 자원으로 바꾸는 방법으로서 명시적 변환을 제공할 것인지 아니면 암시적 변환을 허용할 것인지에 대한 결정은 그 RAII 클래스만의 특정한 용도와 사용 환경에 따라 달라집니다. 어쨌든 가장 잘 설계한 클래스라면 항목 18의 조언을 따라 "맞게 쓰기에는 쉽게, 틀리게 쓰기에는 어렵게" 만들어져야 할 것입니다.

 

꼭 잊지 말아야 할 것!

1. 실제 자원을 직접 접근해야 하는 기존 API들도 많기 때문에, RAII 클래스를 만들 때는 그 클래스가 관리하는 자원을 얻을 수 있는 방법을 열어 주어야 합니다.

2. 자원 접근은 명시적 변환 혹은 암시적 변환을 통해 가능합니다. 안전성만 따지면 명시적 변환이 대체적으로 더 낫지만, 고객 편의성을 놓고 보면 암시적 변환이 괜찮습니다.

반응형

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

[Effective C++] new로 생성한 객체를 스마트 포인터에 저장하는 코드는 별도의 한 문장으로 만들자  (0) 2020.01.17
[Effective C++] new 및 delete를 사용할 때는 형태를 반드시 맞추자  (0) 2020.01.16
[Effective C++] 자원 관리 클래스의 복사 동작에 대해 진지하게 고찰하자  (0) 2020.01.14
[Effective C++] 자원 관리에는 객체가 그만!  (0) 2020.01.13
[Effective C++] 객체의 모든 부분을 빠짐없이 복사하자  (0) 2020.01.12
'언어/C++' 카테고리의 다른 글
  • [Effective C++] new로 생성한 객체를 스마트 포인터에 저장하는 코드는 별도의 한 문장으로 만들자
  • [Effective C++] new 및 delete를 사용할 때는 형태를 반드시 맞추자
  • [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)

블로그 메뉴

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

공지사항

인기 글

태그

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

최근 댓글

최근 글

hELLO · Designed By 정상우.
지나가던 개발자
[Effective C++] 자원 관리 클래스에서 관리되는 자원은 외부에서 접근할 수 있도록 하자
상단으로

티스토리툴바

개인정보

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

단축키

내 블로그

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

블로그 게시글

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

모든 영역

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

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