항목16은 메모리를 해제 할 때 delete를 쓸지 delete[]를 쓸지 그 상황에 맞추어서 잘 쓰라는 내용이었는데 이건 뭐 C++ 기본 문법에 나오는 내용이라 당연한 소리여서 그냥 건너 뛰었다. (물론 책은 읽음)
자, 항목 17은 제대로 안보면 상당히 헷갈리는 부분이다. 우선 아래와 같이 처리 우선 순위를 알려주는 함수가 있고 그 처리 우선 순위에 따라 Widget을 처리해주는 함수가 있다고 치자.
int priority();
void processWidget(std::shared_ptr<Widget> pw, int priority);
그래서 우리는 processWidget 함수를 다음과 같이 호출한다.
void processWidget(new Widget, priority());
하지만 이 코드는 컴파일되지 않는다. 그 이유는 shared_ptr의 생성자는 explicit으로 선언되어 있기 때문이다.
그래서 암시적 변환이 허용되지 않는다. 그래서 우리는 위 코드를 아래와 같이 수정하면 정상 동작 한다.
void processWidget(std::shared_ptr<Widget>(new Widget), priority());
하지만 여기에서는 또 이해할수 없는 사실이 숨어있다. 이 코드는 자원을 흘릴 가능성이 있다는 것이다.
자, 도대체 이게 무슨 소리인가 보자. 솔직히 나도 이런식으로 코드를 많이 작성했었는데 이 책에서 말한것을 보고 좀 충격을 먹었었다. 우선 컴파일러는 processWidget을 호출하기 전에 이 함수의 매개변수로 넘겨지는 인자를 평가하는 순서를 밟는다. 여기서 두 번째 인자는 priority 함수의 호출문밖에 없으나 첫 번째 인자는 new Widget으로 자원을 생성하는 부분, 생성된 자원을 shared_ptr에 넘겨 생성자를 호출하는 두 부분으로 나누어져있다. 그래서 컴파일러는 총 세 가지 연산에 대한 코드를 만들어야 한다. priority 호출, new Widget 실행, shared_ptr 생성자 호출. 그런데 여기서 컴파일러마다 이 세 가지 호출의 실행 순서가 달라질 우려가 있다. 우선 한가지 확실한 것은 new Widget 실행이 shared_ptr 호출보다는 일찍 일어나야 할것이다. 그래서 이런식으로 컴파일러가 순서를 정할 수도 있다. new Widget 실행 -> priority 호출 -> shared_ptr 생성자 호출 순으로 말이다. 그런데 만약 여기서 priority 호출에서 예외가 발생하면 어떻게 될까? 이전에 new Widget이 실행 되었기 때문에 힙 영역에 메모리를 할당한채로 processWidget함수가 종료되어 버려 메모리 누수가 발생할 수 있게된다.
자, 여기까지 자세히 읽었다면 이해를 했을 것이다. 정말 상상하지도 못했던 곳에서 메모리 누수가 발생할 수 있다는 점이 참 놀랍고 이것을 발견한 사람도 참 대단한것 같다. 그렇다면 이 문제는 어떻게 해결할 수 있을까? 방법은 간단하다 우리가 인위적으로 실행 순서를 고정 시켜버리는 것이다. 아래 코드를 보자.
std::shared_ptr<Widget> pw(new Widget);
void processWidget(pw, priority());
자, 이렇게 따로 스마트 포인터를 생성하는 코드를 별도의 문장 하나로 만들고 안전하게 생성된 스마트 포인터를 processWidget이 복사해 가도록 하면 이제는 priority 함수에서 혹여나 예외가 발생하여 processWidget함수가 종료되더라도 스마트 포인터는 이미 생성된 상태라 메모리 누수 걱정없이 안전하게 종료가 가능하다.
자, 솔직히 참 이해하기 까다로운 내용이었지만 앞으로는 코드를 간결하게 하기위해 매개변수 안에서 스마트 포인터를 생성하지 말고 한 줄 더 쓰더라도 new로 생성한 객체를 스마트 포인터에 저장하는 코드는 반드시 별도의 한 문장으로 구분하도록 하자.
'C++ > Effective C++' 카테고리의 다른 글
항목 19 클래스 설계는 타입 설계와 똑같이 취급하자 (0) | 2024.01.20 |
---|---|
항목 18 인터페이스 설계는 제대로 쓰기엔 쉽게, 엉터리로 쓰기엔 어렵게 하자 (0) | 2024.01.20 |
항목 15 자원 관리 클래스에서 관리되는 자원은 외부에서 접근할 수 있도록 하자 (0) | 2024.01.19 |
항목 14 자원 관리 클래스의 복사 동작에 대해 진지하게 고찰하자 (0) | 2024.01.19 |
항목 13 자원 관리에는 객체가 그만! (2) | 2024.01.19 |