728x90
반응형

자원 관리 클래스의 자원 접근

아래 createInvestment로 자원을 만든 후, pInv에 넘겨주는 경우를 생각해보자

shared_ptr<Investment> pInv(createInvestment());

그리고 Investment 객체를 사용하는 함수가 다음과 같이 정의되어있다고 하자

int daysHeld(const Investment *p);
int days = daysHeld(pInv);

그러나 위와 같이 할 경우 컴파일이 되지 않는데, 이유는 Investment* 타입의 포인터를 원하지만 우리가 넘겨주고 있는 타입은 shared_ptr이기 때문이다.

 

RAII 클래스 객체의 자원 변환

위 문제를 해결하기 위해서는 결국 RAII 클래스 객체(shared_ptr)를 실제 자원(Investment*)으로 변환할 방법이 필요하다.
일반적으로 변환을 위한 방법은 다음과 같다.

1. 명시적 변환 (explicit conversion)

shared_ptr 같은 스마트 포인터는 명시적 변환을 수행하는 get이라는 멤버 함수를 제공한다.get 함수를 사용하면 스마트 포인터 객체에 들어있는 실제 포인터(의 사본)를 얻어 낼 수 있다.

int days = daysHeld(pInv.get()); //pInv에 있는 실제 포인터를 넘긴다.

 

2. 암시적 변환 (implicit conversion)

스마트 포인터는 역참조 연산자(operator-> 및 operator*)도 오버 로딩하고 있다.

위 연산자를 통해 관리하고 있는 실제 포인터에 대한 암시적 변환도 쉽게 가능하다.

class Investment
{
    public: 
        bool isTaxFree() const;
}

shared_ptr<Investment> pInv(createInvestment());

bool tax_label1 = !(pInv->isTaxFree()); //operator-> 를 써서 자원에 접근한다.
bool tax_label2 = !((*pInv).isTaxFree());

 

예시

두 가지 변환에 대한 예시와 자원 접근을 제공하는 함수를 만들 때 주의해야 하는 점에 대해서 알아보자.

C API로 직접 조작이 가능한 폰트를 RAII 클래스로 둘러싸서 쓰는 경우에 대한 예시를 든다.

FontHandle getFont(); //C API 함수
void releaseFont(FontHandle f); //C API 함수

class Font 
{
    public:
        explict Font(FontHandle fh):f(fh) {} //자원 획득
         ~Font() { releaseFont(f) };

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

 

명시적 변환

Font 객체를 FontHandle로 변환해주는 get 함수를 제공한다 하자.

class Font
{
public: 
    FontHandle get() const { return f; } //명시적 변환 함수
}

이렇게 하면 사용 시에 get을 호출해서 명시적으로 변환하면 되는데,

void ChangeFontSize(FontHandle f, int newSize);
Font f(getFont());
int newFontSize;

ChangeFontSize(f.get(), newFontSize); //Font 에서 FontHandle 로 명시적 변환 후에 넘긴다.

이로 인해서 변환할 때마다 get 함수를 호출해야 하는 번거로움이 발생한다.

 

암시적 변환

operator()를 정의해서 암시적으로 Font를 FontHandle로 변환하는 함수를 제공한다.

class Font 
{
public: 
    operator FontHandle() const { return f }; //암시적 변환 함수
}

이렇게 암시적인 변환은 아래와 같이 바로 사용하면 된다.

Font f1(getFont());
int newFontSize;
ChangeFontSize(f1,newFontSize);

하지만 문제점이 하나 존재하는데, 진짜 Font를 복사하려고 한 부분에서 FontHandle로 바뀔 수 있는 문제가 발생한다.

Font f1(getFont());
FontHandle f2= f1; //Font객체를 복사하려고 했는데, FontHandle로 바뀌고 나서 복사 되었다.

또한 이렇게 하면 f1이 관리하고 있는 FontHandle를 f2에서도 직접 사용할 수 있는 형태가 되기 때문에 f1에서 FontHandle을 해제하면, f2는 해제된 자원을 갖게 된다.

 

결론적으로, RAII클래스를 실제 자원으로 바꾸는 방법으로서 명시적 변환 혹은 암시적 변환을 제공할 것인지에 관한 것은 해당 RAII클래스만의 특정한 용도와 환경에 따라 달라진다.

그리고 RAII클래스는 애초부터 데이터 은닉이 목적이 아니기 때문에 캡슐화에 위배는 설계는 아니다.

 

요약

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