new 로 생성한 객체를 스마트 포인터에 저장할 때 발생할 수 있는 문제
int priority(); // 우선순위를 반환하는 함수
void processWidget(std::tr1::shared_ptr<Widget> pw, int priority);
processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());
- 위 코드는 컴파일은 되지만, 예외가 발생하면 리소스 누수가 발생할 가능성이 있음
Order of Evaluation of Arguments (c++의 인자 평가 순서)
- c++에서는 함수의 인자 평가 순서가 정의되어 있지 않음.
processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());
- 위 코드에서 컴파일러는 인자의 평가 순서를 마음대로 선택할 수 있다는 것
new Widget은 반드시 먼저 실행되어야 한다.
- 하지만
priority() 함수 호출과 std::shared_ptr 생성 순서는 컴파일러가 임의로 정할 수 있음.
- 만약
priority() 함수가 두 번째로 실행되고, 예외(exception)를 던진다면?
new Widget으로 생성한 객체는 스마트 포인터에 저장되지 않음.
- 결과적으로 메모리 누수(memory leak)가 발생
해결 방법 : new 로 생성한 객체를 먼저 스마트 포인터에 저장
std::tr1::shared_ptr<Widget> pw(new Widget); // 스마트 포인터에 먼저 저장
processWidget(pw, priority()); // 안전한 호출
- C++에서는 하나의 문장에서 여러 작업을 처리하는 것보다, 중요 리소스를 먼저 안전하게 저장하는 것이 더 좋다.
std::make_shared 를 활용한 더 안전한 방법
- c++ 11 이상에서는
std::make_shared 를 사용하는 것이 더 안전하고 효율적
auto pw = std::make_shared<Widget>(); // 안전한 스마트 포인터 생성
processWidget(pw, priority()); // 예외 발생해도 안전
std::make_shared<T>()는 new를 사용하지 않고, 객체와 shared_ptr을 한 번에 생성하므로 더 안전하다.
- 메모리 할당이 한 번만 발생하므로 성능이 더 좋다.
- 예외가 발생해도 객체가 안전하게 스마트 포인터에 저장됨.