스콧 마이어스의 Effective C++을 읽고 개인 공부 목적으로 요약 작성한 글입니다!
💡 new로 생성한 객체를 스마트 포인터로 넣는 코드는 별도의 한 문장으로 만들자!
이렇게 안하면, 예외 발생 시 디버깅 하기 힘든 자원 누출이 발생할 수 있다
int priority();
void processWidget(std::tr1::shared_ptr<Widget> pw, int priority);
processWidget(new Widget, priority());
이런 경우에는, new Widget으로 생성한 포인터
와 tr1::shared_ptr<Widget>
가 다른 타입이고,
그 포인터를 shared_ptr타입으로 바꿔주는 암시적인 변환이 없기 때문에
컴파일이 안된다.
shared_ptr의 생성자는 explicit으로 선언되어 있다..
explicit
: 컴파일러가 형변환 해주는 것을 막아준다.
그래서
processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());
이렇게 해줘야 한다.
(shard_ptr 타입으로 명시적 변환)
근데 이러한 상황에서는 자원이 누출될 가능성이 있다.
컴파일러는 processWidget 호출 코드를 만들기 전에 인자를 평가한다.
그래서 함수를 호출하기 전에, 컴파일러는 아래 세 가지 연산을 위한 코드를 만들어야 한다.
priority 호출
new Widget 실행
tr1::shared_ptr 생성자 호출
근데 C++ 컴파일러는 위 세 가지 연산의 수행 순서가 미정의되어있다.
아무래도, shared_ptr
의 생성자의 인자로 new Widget
한 결과가 들어가야 하니까
new Widget
표현식 실행이 tr1::shared_ptr
생성자 호출보다 선행되어야 한다.
근데,,
priority
는 언제 호출되든 상관없잖아
그래서 만약에
new Widget 실행
priority 호출
tr1::shared_ptr 생성자 호출
순서대로 이렇게 수행된다고 했을 때,
만약 priority 호출
에서 문제가 생긴다면,,
당연히 tr1::shared_ptr 생성자 호출
은 불리지 않을 거고
그러면 new Widget
으로 생성된 포인터는 .. 그냥 누출될거다.
왜그러냐면,
해결하려면,,
std::tr1::shared_ptr<Widget> pw(new Widget);
processWidget(pw, priority());
이렇게 한다면
pw
나 priority 호출
두 개의 순서만 왔다갔다하고,
생성되는 시점과 그 객체를 자원 관리 객체(shared_ptr)에 넣어주는 시점 사이가 방해받을 일은 없다.
그래서 자원이 누출될 일이 없는거당.
😊 느낀점
생성 시점, 자원 관리 객체로 넘겨주는 시점
두 시점 사이에 다른 일이 끼어들어서 자원이 누출되는 상황
디버깅한다고 생각하면 진심 끔찍하다
미리 미연에 방지하도록 하자,,