우리가 shared_ptr로 객체를 만들고 그 객체 안에 있는 포인터를 매개변수로 넣어 주려고 한적 있을 것이다. 근데 그건 안된다. 암시적인 형변환이 안되기 때문에 get() 함수를 써야지 인자를 제대로 넘겨줄 수 있다.
class TEST1
{
private:
int i;
public:
TEST1():i(7777) {}
TEST1(const TEST1& rhs) {}
void TestFunc(const TEST1* const rhs) const
{
cout << rhs->i << endl;
}
};
class TEST2 : public TEST1
{
private:
int i;
public:
TEST2() {}
TEST2(const TEST2& rhs) {}
};
int _tmain()
{
shared_ptr<TEST1> t1(new TEST2);
//t1->TestFunc(t1); //error
t1->TestFunc(t1.get()); //이건 정상작동
//t1에 들어있는 실제 포인터를TestFunc에 넘김
}
왜냐하면 코드상의 t1은 shared_ptr"클래스"로 만들어진 것이다 그러기 때문에 형이 다르다. 하지만 get() 함수를 쓰면 TEST 1의 주소를 넘겨준다.
클래스 안에 있는 자원을 밖에서 많이 쓰면 밖에서도 쓸수 있게끔 하는 것이 좋다. 클래스는 캡슐화해야 한다고 하는데 그것도 맞긴 맞지만 사용자 입장에서 생각해 봐야 한다. 빵으로 비유를 하자면 사용자라는 손님이 빵을 어떻게 만드느냐고 물어볼 때 외부에 접근 가능한 자원은 밀가루, 베이킹파우더, 설탕 같은 손님이 알아도 장사에 지장이 없는 재료들이고 캡슐화해야 할 자원은 특제소스, 나만의 비밀 레시피 같은 손님이 알아선 안 되는 재료들이다. 다음 C API의 Font의 소스를 봐보자
FontHandle getFont(); //C API에서 가져온 함수 -- 매개변수는 생략
void releaseFont(FontHandle fh);
class Font //RAII클래스
{
public:
explicit Font(FontHandle fh) //자원을 획득한다. 여기서
: f(fh) //값에 의한 전달이 수행되는 것에
{} //주의한다
~Font() { releaseFont(f); } //C API로 하기 때문이다.
FontHandle get() const { return f; } //명시적 반환 함수
};
void changeFontSize(FontHandle f, int newSize); //폰트 API의 일부
int _tmain()
{
Font f(getFont());
int newFontSize;
...;
changeFontSize(f.get(), newFontSize); //Font에서 FontHandle로
//명시적으로 바꾼 후에 넘긴다.
}
이 코드들을 보면 FontHandle을 get()함수로 "명시적" 변환을 하는 것을 볼 수 있다. 아까 말했듯이 사용자가 자주 써야 할 것 같은 거는 꼭 캡슐화를 해야 할 거 아니면 제공해 주자 그리고 아까 "명시적"이라고 했는데 "묵시적"도 가능하다. 다음 코드를 봐보자
FontHandle getFont(); //C API에서 가져온 함수 -- 매개변수는 생략
void releaseFont(FontHandle fh);
class Font //RAII클래스
{
public:
.....;
operator FontHandle() { return f; } //암시적 변환
.....;
};
void changeFontSize(FontHandle f, int newSize); //폰트 API의 일부
int _tmain()
{
Font f(getFont());
int newFontSize;
...;
changeFontSize(f, newFontSize); //Font에서 FontHandle로
//암시적으로 바꾼 후에 넘긴다.
}
이렇게 보면 묵시적인게 편하고 좋을 것 같지만 꼭 그렇지만은 않다 실수할 확률이 올라가기 때문이다. 실수의 예를 봐보자
Font f1(getFont());
FontHandle f2 = f1; //원래는 Font 객체를 복사하려고 했는데
//f1이 자동으로 FontHandle로 바뀌어서
//복사되어버렸다
쨋든 결론적으로 중요한것은 자원관리 클래스에서 제공해줄 필요 없는 것들은 숨기고 보여줘도 상관없는 것들은 제공해주자.
이것만은 잊지 말자!
1. 실제 자원은 직접 접근해야 하는 기존 API들도 많기 때문에, RAII클래스를 만들 때는 그 클래스가 관리하는 자원을 얻을 수 있는 방법을 열어 주어야 한다.
2. 자원 접근은 명시적 변환 혹은 암시적 변환을 통해 가능하다. 안정성만 따지면 명시적 변환이 대체적으로 더 낫지만, 고객 편의성을 놓고 보면 암시적 변환이 괜찮다.