본문 바로가기
카테고리 없음

[Effective C++ 정리 #17] 스마트 포인터로 감싸도 new는 독립적으로 먼저 받아라

by 허구의 2025. 6. 27.
728x90

이 글은 『Effective C++』를 읽고 개인적으로 공부한 내용을 정리한 기록입니다.

저는 컴퓨터공학을 전공하지 않았으며, 프로그래밍을 공부하는 과정에서의 이해와 생각을 정리하기 위해 글을 작성하고 있습니다.

따라서 내용 중 일부에 오류나 부정확한 설명이 있을 수 있으며, 피드백은 언제든지 환영합니다. 확인 후 수정하도록 하겠습니다.

전문적인 해설이 아닌 개인적 시선에서의 정리임을 참고하고 읽어주시면 감사하겠습니다.

[Effective C++ 정리 #17] 스마트 포인터를 사용할 때 new는 반드시 독립된 문장에서 처리합니다

스마트 포인터를 사용하면 자원 해제 문제를 거의 완벽하게 해결할 수 있습니다. 하지만 이번 아이템 #17에서는 한 가지 놓치기 쉬운 중요한 내용을 알려줍니다. 그것은 바로, 스마트 포인터를 사용해도 new 연산은 반드시 독립된 문장에서 처리해야 예외에 안전하다는 것 입니다.


문제 상황: processWidget 예제

다음과 같은 함수를 생각해봅니다:

int priority();
void processWidget(std::tr1::shared_ptr<Widget> pw, int priority);

 

RAII를 활용하여 shared_ptr로 자원을 관리하고 있습니다. 이제 우리는 다음처럼 호출하고 싶어집니다:

processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());

 

일견 아무 문제가 없어 보입니다. new Widget으로 객체를 생성하고, 곧바로 shared_ptr에 넘겨서 소유권을 위임합니다. 이러면 자원 누수 걱정은 없는 것처럼 보입니다.

 

하지만 **예외가 발생하면 심각한 문제가 발생할 수 있습니다**.


C++는 인자 평가 순서를 보장하지 않습니다

C++에서는 함수 인자의 평가 순서가 명시되어 있지 않습니다. 따라서 컴파일러는 다음 작업들을 어떤 순서로든 수행할 수 있습니다:

  1. priority() 호출
  2. new Widget 실행
  3. shared_ptr 생성자 호출

물론 shared_ptr 생성자는 new Widget의 결과를 받아야 하므로 최소한 new Widgetshared_ptr 생성자 순서는 보장됩니다. 하지만 priority()는 어디서든 호출될 수 있습니다. 심지어 new Widgetshared_ptr 생성 사이에 끼어들 수도 있습니다.


예외 발생 시 발생하는 치명적 자원 누수

만약 다음 순서로 실행된다면 어떻게 될까요?

  1. new Widget 실행 — 객체 메모리 할당
  2. priority() 호출 중 예외 발생

이 경우 new Widget이 생성한 포인터는 아직 shared_ptr에 전달되지 않았습니다. 따라서 예외가 발생하면 이 포인터는 아무도 관리하지 않게 되어 메모리 누수(resource leak)가 발생합니다.

**스마트 포인터를 사용해도 예외 발생 위치에 따라 자원이 유실될 수 있다는 사실**을 꼭 기억해야 합니다.


해결책: new는 반드시 독립된 문장에서 수행합니다!

이 문제를 해결하는 방법은 간단합니다. new 연산과 shared_ptr 생성은 독립된 문장으로 분리합니다:

std::tr1::shared_ptr<Widget> pw(new Widget);
processWidget(pw, priority());

 

이렇게 하면 new Widgetshared_ptr 생성이 완전히 끝난 후에 priority()가 호출되므로 예외가 발생해도 shared_ptr가 자원을 관리하고 있어 안전합니다.

 

스마트 포인터를 사용한다고 해서 모든 예외 안전성이 보장되는 것은 아닙니다. **스마트 포인터에 소유권이 넘어가기 전까지는 여전히 위험 구간이 존재합니다.** 따라서 본 아이템에서는 그 위험 구간을 어떻게 제거하는지 말해주고 있습니다.


핵심 요약

  • new로 생성한 객체를 스마트 포인터에 넘길 때는 반드시 독립된 문장에서 처리한다.

감사합니다.

728x90