자원 누출 가능성
스마트 포인터 생성 코드
처리 우선순위를 알려주는 함수와
동적으로 할당한 Widget 객체에 대해 우선순위에 따라 처리를 적용하는 함수가 있다고 하자
int priority();
void processWidget(std::shard_ptr<Widget> pw, int priority); //shared_ptr 을 통해 자원관리
호출시 아래와 같이 쓰면 컴파일 에러가 난다!
processWidget(new Widget, priority());
shared_ptr 의 생성자는 explicit 로 선언되어 있기 때문에,new Widget
에 의해 만들어진 포인터가 shared_ptr 타입의 객체로 바뀌는 암시적 변환은 일어나지 않는다.
그래서 아래와 같이 써줘야 한다.
processWidget(shared_ptr<Widget> (new Widget), priority());
그럼에도 불구하고....
저자는 위의 문장이 눈물을 흘릴 수가 있다고 한다. 흑흑
왜 그럴까?
컴파일러의 동작
컴파일러는 processWidget 호출 코드를 만들기 전에, 이 함수의 매개변수로 넘겨지는 인자를 평가(evaluate) 한다.
두번째 인자는 priority() 의 함수 호출문이 끝이다.
하지만, 첫번째 인자 shared_ptr<Widget> (new Widget)
은 두 부분으로 나눠져 있다.
new Widget
표현식을 실행하는 부분shared_ptr
생성자를 호출하는 부분
그래서 컴파일러는 함수 호출이 이뤄지기 전, 다음의 세 가지 연산을 위한 코드를 만들어야 한다.
- priority 를 호출한다.
new Widget
을 실행한다.shared_ptr
생성자를 호출한다.
그러나 각각의 연산이 실행되는 순서는 컴파일러 제작사마다 다르다.
c++ 컴파일러의 경우엔 이 순서를 정하는데 있어서 상당한 자유도를 가지고 있다.
(자바나 c# 은 매개변수 평가(evalute) 순서가 고정되어 있다.)
new Widget
은 shard_ptr 생성자 호출전에 불린다. shared_ptr 생성자의 인자로 넘어가니까 이건 당연하다.
그러나 priority 의 호출은 언제 될지 모른다. 어떤 컴파일러에서는 다음과 같은 순서일 수도 있다.
new Widget
을 실행한다.- priority 를 호출한다.
- shared_ptr 생성자를 호출한다.
근데 여기서 priority 호출 부분에서 예외가 발생한다면?new Widget
으로 만들어졌던 포인터가 유실된다. 자원이 누출된다!!
정리하면 processWidget 호출 중 자원이 노출될 가능성이 있는 것은,
자원이 생성되는 시점과 자원이 관리 객체로 넘어가는 시점 사이에 예외가 낄 수 있기 때문이다.
해결책
간단하다.
Widget 을 생성해서 스마트 포인터에 저장하는 코드를 별도의 문장 하나로 만들고,
그 스마트 포인터를 processWidget 에게 넘기는 것!
shared_ptr<Widget> pw(new Widget);
processWidget(pw,priority());
요약
new 로 생성한 객체를 스마트 포인터로 넣는 코드는 별도의 한 문장으로 만들자.
이것이 안되어 있으면, 예외가 발생할 때 디버깅하기 힘든 자원 누출이 초래될 수 있다.
'C++ > Effective C++' 카테고리의 다른 글
항목 16: new 및 delete 를 사용할 때는 형태를 반드시 맞추자 (0) | 2020.09.24 |
---|---|
항목 15: 자원 관리 클래스에서 관리되는 자원은 외부에서 접근할 수 있도록 하자 (0) | 2020.09.24 |
항목 14: 자원 관리 클래스의 복사 동작에 대해 진지하게 고찰하자 (0) | 2020.09.23 |
항목 13: 자원 관리에는 객체가 그만! (0) | 2020.09.23 |
항목 31 : 파일사이의 컴파일 의존성을 줄이자 (0) | 2020.09.22 |