KoreanFoodie's Study

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

Tutorials/C++ : Advanced

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

GoldGiver 2022. 10. 25. 16:10

C++ 프로그래머의 필독서이자 바이블인, 스콧 마이어스의 Modern Effective C++ 를 읽고 기억할 내용을 요약하고 있습니다. 꼭 읽어보시길 추천드립니다!

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

핵심 :

1. 실제 자원을 직접 접근해야 하는 기존 API 들도 많기 때문에, RAII 클래스를 만들 때는 그 클래스가 관리하는 자원을 얻을 수 있는 방법을 열어 주어야 한다.
2. 자원 접근은 명시적 변환 혹은 암시적 변환을 통해 가능하다. 안전성만 따지면 명시적 변환이 대체적으로 더 낫지만, 고객 편의성을 놓고 보면 암시적 변환이 괜찮다.

 

다음의 경우처럼, RAII 클래스의 객체를 그 객제가 감싸고 있는 실제 자원으로 변환해야 하는 경우가 종종 생긴다.

std::shared_ptr<Investment> pInv(CreateInvestment());
// 투자금이 유입된 이후로 경과된 날수를 구하는 함수
int daysHeld(const Investment *pi);

...

// 에러! *Investment 가 들어가야 함!
int days = daysHeld(pInv);

// 명시적 변환! pInv 의 실제 포인터를 넘김
int days = daysHeld(pInv.get());

// 암시적 변환! pInv 의 실제 포인터를 넘김
int days = daysHeld(*pInv);

/* 또 다른 예시 */
// 팩토리 함수
Investment* createInvestment();

// shared_ptr 로 자원 관리
std::shared_ptr<Investment> pi1(createInvestment());
// operator -> 를 사용하여 자원에 접근
bool taxable1 = !(pi1->isTaxFree());

// unique_ptr 로 자원 관리
std::unique_ptr<Investment> pi2(createInvestment());
// operator* 를 써서 자원에 접근
bool taxable2 = !((*pi2).isTaxFree());

 

그렇다면 명시적 변환이 좋을까, 암시적 변환이 좋을까? 다음 예시를 보자.

// C API 에서 가져온 함수들
FontHandle getFont();
void releaseFont(FontHandle fh);

// RAII 클래스
class Font
{
  public:
    // 자원획득 (값에 의한 전달)
    explicit Font(FontHandle fh)
    : f(fh)
    {}
    ~Font() { releaseFont(f); }

    // 1. 명시적 변환 함수
    FontHandle get() { return f; }
    // 2. 암시적 변환 함수
    operator FontHandle() const { return f; }

  private:
    // 실제 폰트 자원
    FontHandle f;
};

void changeFontSize(FontHandle f, int newSize);

int main()
{
  // 실제 Font 클래스 사용 예시
  Font f(getFont());
  int newFontSize;
  
  // 1. 명시적 변환 함수 사용
  changeFontSize(f.get(), newFontSize);
  // 2. 암시적 변환 함수 사용
  changeFontSize(f, newFontSize);
  
  Font f1(getFont());
  // 헉! 원래 의도는 Font 객체를 복사하는 것이었는데,
  // f1 이 FontHandle 로 바뀌고 나서 복사되었다! (암시적 변환)
  FontHandle f2 = f1;
}

위의 예시처럼, 암시적 변환을 쓰면 get( ) 같은 메소드를 추가로 호출하지 않아도 되어 간편하지만, 실수가 맨 아래의 FontHandle 형 변환의 경우처럼, 의도치 않은 형 변환이 일어날 수 있는 가능성이 있다!

따라서 RAII 클래스를 실제 자원으로 바꾸는 방법으로서 명시적 변환을 제공할 것인지, 암시적 변환을 허용할 것인지에 대한 결정은 그 RAII 클래스만의 특정한 용도와 사용 환경에 따라 달라질 것이다!

Comments