[Effective C++] 15. 자원 관리 클래스에서 관리되는 자원은 외부에서 접근할 수 있도록 하자
Effective C++ 제 3판 - Chapter 3 - 3
RAII 클래스를 사용할 때면 내부의 자원을 직접 참조해야하는 경우가 많습니다. 그래서 get()과 같은 메소드를 만들어서 내부의 자원을 직접 가져와 사용하도록 만드는 분들이 많지요.
또한 더 고급적인 코딩 스킬을 사용하여 RAII 클래스 걸계자 중에 암시적 변환 함수를 제공하여 자원 접근을 매끄럽게 할 수 있도록 만드는 분도 있습니다.
예를 들어 어떤 하부 수준 C API로 직접 조작이 가능한 폰트를 RAII 클래스로 둘러싸서 쓰는 경우를 생각해 봅시다.
명시적 & 암시적 변환 방법
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
typedef int FontHandle;
FontHandle getFont()
{
FontHandle dummyFontHandle = 25;
return dummyFontHandle;
}
class Font
{
public:
explicit Font(FontHandle fh)
: m_fontHandle(fh)
{}
~Font()
{
releaseFont(m_fontHandle);
}
void releaseFont(FontHandle fh);
FontHandle get() const;
operator FontHandle() const;
private:
FontHandle m_fontHandle;
};
void Font::releaseFont(FontHandle fh)
{
std::cout << "release fontHandle : " << fh << std::endl;
}
FontHandle Font::get() const
{
return m_fontHandle;
}
Font::operator FontHandle() const
{
return m_fontHandle;
}
int main()
{
Font f1(getFont()); // 10
FontHandle f2 = f1;
std::cout << "f1(Font).FontHandle : " << f2 << std::endl;
std::cout << "f2(FontHandle) : " << f2 << std::endl;
}
위의 예를 보시면 get()은 명시적 형변환이고, operator()를 사용한 변환은 암시적 형변환 입니다. 예제만으로 다른 설명이 필요 없을 정도로 쉽습니다.
다만 암시적 형변환이 명시적 형변환보다 항상 더 나은 선택이라고는 할 수 없습니다. 상황에 알맞게 선택하여 사용하는 것이 좋겠지요.
캡슐화에 대한 고민
단, 이렇게 되면 Font 객체인 f1이 관리하고 있는 FontHandle이 f2를 통해서도 직접 사용할 수 있는 상태가 됩니다. 객체 지향에 대해 조금이라도 알고 계신다면 앞서 말한 상황이 캡슐화에 위배되는 것이 아닌가하고 고민해볼 수 있겠지요. 하지만 괜찮습니다!. 표준라이브러리를 포함하여 이미 많은 곳에서 그러한 느슨한 방법을 사용 중입니다.
그러니 RAII 클래스 설계시에는 RAII가 관리하고 있는 자원에 직접 접근할 수 있는 방법을 마련해주는 것이 좋습니다. 스마트 포인터에서는 get()이라는 명시적 변환을 수행하는 함수가 이미 있습니다.
End Note
- 실제 자원을 직접 인자로 사용해야 하는 기존 API들도 많기 때문에, RAII 클래스를 만들 때는 그 클래스가 관리하는 자원을 얻을 수 있는 방법을 열어 주어야 합니다.
- 자원 접근은 명시적 변환 혹은 암시적 변환을 통해 가능합니다. 안전성만 따지면 명시적 변환이 대체적으로 더 낫지만, 고객 편의성을 놓고 보면 암시적 변환이 괜찮습니다.
Reference
- Effective C++ (Scott Meyers)
Leave a comment