// 처리 우선순위를 반환하는 함수
int priority();
// 동적 할당한 Widget 객체를 우선순위에 따라 처리하는 함수
void processWidget(std::shared_ptr<Widget> pw, int priority);
자원을 관리할 때는 객체를 사용하면 좋다고 배웠다.(항목 13)
따라서 processWidget
함수를 동적 할당된 Widget
객체에 대한 스마트 포인터를 사용하도록 만들었다.
processWidget(new Widget, priority());
processWidget
함수를 호출하는 이 코드는 컴파일 에러가 발생한다.
shared_ptr
는 생성자가 explicit
으로 선언되어 있기 때문에 new Widget
으로 만들어진 포인터가 shared_ptr
타입 객체로 바뀌는 암시적이 변환이 이루어지지 않기 때문이다.
processWidget(shared_ptr<Widget>(new Widget), priority());
정상적으로 컴파일이 되려면 명시적으로 new Widget
으로 만들어진 포인터를 shared_ptr
타입 객체로 변환해야 한다.
그러나 이 코드는 자원을 흘릴 가능성이 있다.
컴파일러는 processWidget
호출 코드를 만들기 전에 먼저 processWidget
함수의 매개변수로 넘겨진 인사를 평가한다.
이때 두 번째 인자는 priority 호출 뿐이지만, 첫 번째 인자는 두 부분으로 나누어져 있다.
shared_ptr<Widget>(new Widget)
new Widget
표현식 실행priority()
priority
호출그런대 이 세 가지 연산이 실행되는 순서는 컴파일러 제작사마다 다르다. (특히 C++은 평가 순서에 대해 상당한 자유도를 갖고 있다.)
물론 shared_ptr의 생성자 호출이 new Widget
실행 이후라는 것은 고정이지만, priority
호출은 처음, 혹은 두 번째, 아니면 세 번째에 호출될 수 있다.
new Widget
표현식 실행priority
호출만약 다음과 같이 컴파일러가 priority
호출을 두 번째로 정했다면 자원이 누출될 가능성이 생긴다.
priority
호출에서 예외가 발생한다면, new Widget
으로 생성한 포인터가 유실되기 때문이다.
자원 누출을 막으려면, 자원이 생성되는 시점과 그 자원이 자원 관리 객체로 넘어가는 시점 사이에 예외가 끼어들지 못하도록 해야 한다.
// new로 생성한 객체를 스마트 포인터에 저장하는 코드를 별도의 문장으로 분리
shared_ptr<Widget> pw(new Widget);
processWidget(pw, priority()); // 자원 누출 가능성 X
자원을 스마트 포인터에 저장하는 코드를 별도의 문장으로 만들자.
정리
함수의 인자 평가는 랜덤이므로 자원 누출의 가능성을 없애려면 자원을 생성하고 스마트 포인터에 저장하는 코드를 별도의 문장으로 만들자