1. 개요
이번 장의 내용은 컴파일러마다 실행순서가 달라져서 발생하는 문제를 막는 방법에 대해 소개하는 항목입니다.
예를 들어보도록 하겠습니다.
int Priority();
void ProcessWidget(std::tr1::shared_ptr<Widget> pw, int priority);
이러한 함수 2개가 있다고 가정해 보겠습니다.
그리고 이렇게 만들어진 함수를 호출합니다.
processWidget(new Widget, priority());
일단 이것은 틀렸습니다.
왜냐하면 shared_ptr로 받고있기 때문에 자동으로 형변환이 안됩니다.
(shared_ptr의 생성자는 explicit으로 선언되어 있습니다.)
그래서 아래와 같이 써야합니다.
processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());
그러나 이것은 컴파일은 되지만 자원을 흘릴 가능성이 남아있습니다.
2. 흘릴 가능성 분석
위의 함수를 실행할떄 컴파일러는 총 3가지 연산을하는 코드를 만들어야 합니다.
- priority를 호출
- new Widget 실행
- tr1::shared_ptr의 생성자 호출
그러나 핵심은 컴파일러마다 이것들을 처리하는 순서가 달라질 수 있다는 것입니다.
일단 new Widget은 반드시 shared_ptr의 생성자보다 먼저 호출됩니다.
(new Widet이 인자로 넘어가는 부분이기 때문이죠)
그러나 priority의 순서는 모호합니다.
만약 순서가 이렇게 정해졌다고 해보겠습니다.
1. new Widget 실행
2. priority 호출
3. shared_ptr 생성자 호출
그런데 여기서 2번에서 예외가 발생했다고 생각해봅시다.
스마트포인터에 저장되기 전에 예외가 발생하게 됐고, 자원은 누수됩니다.
이런식으로 누수된 자원은 찾기에 매우 힘이 듭니다.
3. 결론
이러한 위험을 피하는 방법은 매우 간단합니다.
std::tr1::shared_ptr<Widget> pw(new Widget);
processWidget(pw,priority());
이렇게 스마트 포인터에 담는 코드를 독립적으로 만드는 것입니다.
이렇게 하면 priority의 호출이 new Widget과 shared_ptr 생성자 사이에 끼어들 일이 없습니다.
이것만은 잊지 말자!
- new로 생성한 객체를 스마트 포인터로 넣는 코드는 별도의 한 문장으로 만듭시다. 이것이 안 되어 있으면, 예외가 발생될 때 디버깅하기 힘든 자원 누출이 초래될 수 있습니다.
https://yeoul0714.tistory.com/59
[Effective C++] 항목 18: 인터페이스 설계는 제대로 쓰기엔 쉽게, 엉터리로 쓰기엔 어렵게 하자
1. 개요 우리는 C++로 개발을 하면서 정말 수많은 인터페이스를 설계하게 됩니다. 그리고 우리가 개발한 인터페이스가 올바르게 쓰여지길 바랍니다. 만약 올바르게 쓰여지지 않았다면 최소한의
yeoul0714.tistory.com
'Effective C++ > Chapter 3: 자원관리' 카테고리의 다른 글
[Effective C++] 항목 16: new 및 delete를 사용할 때는 형태를 반드시 맞추자 (0) | 2025.05.02 |
---|---|
[Effective C++] 항목 15: 자원 관리 클래스에서 관리되는 자원은 외부에서 접근할 수 있도록 하자 (0) | 2025.04.29 |
[Effective C++] 항목 14: 자원 관리 클래스의 복사 동작에 대해 진지하게 고찰하자 (0) | 2025.04.29 |
[Effective C++] 항목 13: 자원 관리에는 객체가 그만! (0) | 2025.04.26 |