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

업데이트:     Updated:

카테고리:

태그:

이 글은 아래의 책을 자세히 정리한 후, 정리한 글을 GPT에게 요약을 요청하여 작성되었습니다.
이펙티브 C++ 제3판, 스콧 마이어스 저자, 곽용재 번역

📦 3. 자원 관리

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

shared_ptr<Investment> pInv(createInvestment());

int daysHeld(const Investment *pi);

이렇게 함수 선언과 자원 생성이 돼 있다고 하자.

근데 아래처럼 쓰면?

int days = daysHeld(pInv);

컴파일 에러가 난다.

pInv는 shared_ptr이고, daysHeld는 Investment*를 받기 때문이다.

int days = daysHeld(pInv.get());

shared_ptr에 들어있는 실제 포인터를 꺼내기 위해선 위와 같이 짜야한다.

이때 pInv.get() 과 같은 방식으로 형을 변경하는 방식을
명시적 변환(explicit conversion)이라고 한다.

암시적 변환도 있는데 이는 다음 코드를 보면서 알아보자.


🔃 명시적, 암시적 변환

클래스가 변환 연산자를 제공하면, 암시적 변환(implicit conversion)도 가능하다.

// 아래 코드는 C API에서 가져온 함수
// 실제로 있는 함수는 아닌듯 하다.
// (매개 변수, 구현부 생략됨)
FontHandle getFont();
void releaseFont(FontHandle fh);

// 폰트 API의 일부
void changeFontSize(FontHandle f, int newSize);

// RAII 클래스
class Font {
public:
	explicit Font(FontHandle fh) // 자원 획득
	: f(fh) {}
	~Font() { // 자원 반환
		releaseFont(f);
	}
	
	FontHandle get() const { return f; } // 명시적 변환
	operator FontHandle() const { return f; } // 암시적 변환

private:
	FontHandle f;
};

Font 클래스는 FontHandle이라는 자원을 RAII 방식으로 감싸고 있다.

생성 시 자원을 획득하고, 소멸 시 자원을 해제한다.


🧪 변환 예시

int main() {
	Font f(getFont());
	int newFontSize;
	
	// *첫번째 상황* : Font f 객체에서 FontHandle 꺼내기
	
	// 명시적 변환
	changeFontSize(f.get(), newFontSize);
	// 암시적 변환
	changeFontSize(f, newFontSize);

변환 연산자 operator FontHandle() 덕분에 암시적으로 변환이 되고 있다.

💣 문제의 상황

// *두번째 상황*: Font f2 = f1
	
Font f1(getFont());
// 의도는 Font 객체를 복사하는 것
// Font f2 = f1;와 같이 짰어야했다!
FontHandle f2 = f1;

이런 상황을 조심해야한다.

의도는 Font 객체를 복사하고 싶었던 거지만,
실제로는 암시적 변환이 일어나서 FontHandle만 복사된다.

결과적으로 f1이 소멸되면 FontHandle도 해제되는데,
f2는 이미 해제된 자원을 계속 가리키는 셈이다.

이는 자원 관리 객체의 목적과 상충된다.


🧭 어떤 변환을 쓸 것인가?

RAII 클래스에서 자원을 외부에 노출할 때는 두 가지 방식이 있다:

  1. .get()처럼 명시적 접근자 제공
  2. operator T()처럼 암시적 변환 연산자 정의

근데 이것은 클래스의 목적에 따라 달라진다.

  • 단순한 포장(wrapper) 클래스처럼 쓸 거면 암시적 변환도 괜찮음
  • 자원을 명확히 관리하는 책임이 중요하다면, 명시적 변환이 더 안전함

특히, 복사가 오용될 수 있는 자원이라면 암시적 변환은 피하는 게 좋다.

RAII 클래스를 실제 자원으로 바꾸는 방법으로
명시적 변환을 사용할 것인가, 암시적 변환을 사용할 것인가는

→ RAII 클래스의 특정 사용 용도와 사용 환경에 따라 달라진다.


🧐 정리

  1. RAII 클래스를 만들 때는 그 클래스가 관리하는 자원을 얻을 수 있는 방법을 열어주자.
  2. 자원 접근은 명시적/암시적 변환을 통해 가능하다.
    안정성은 명시적 변환, 고객 편의성은 암시적 변환이 좋다.

Cpp 카테고리 내 다른 글 보러가기

댓글남기기