본문 바로가기

C++/Effective C++

항목 13: 자원 관리에는 객체가 그만!

참고 : 책에서는 auto_ptr 을 예로 들어서 계속 설명하지만, auto_ptr 은 사용권장 되고 있지 않기 때문에 관련한 부분은 여기에 적지 않았다.

스마트 포인터

동적 할당한 객체의 문제

아래와 같이 동적할당한 객체를 얻어내고, 해당 객체를 사용한 뒤에 delete 해주는 함수가 있다하자.

void f() {
    Investment *pInv = createInvestment();
    ...
    delete pInv;
}

이 경우 해당 객체를 삭제 하지 못하는 경우가 발생할 수 있다.

1) delete 이전에 함수가 리턴되는 상황
2) 도중에 예외가 던져져서 delete를 하지 못하는 상황


createInvestment 로 얻어낸 자원이 항상 해제되도록 하는 방법은,
자원을 객체에 넣고 그 자원을 소멸자가 맡도록 하며, 그 소멸자는 실행 제어가 f 를 떠날때 호출되도록 만드는 것이다.
c++ 에서는 이런 객체를 지원해주는데, 그렇다. 바로 스마트 포인터(smart pointer) 이다.


스마트 포인터를 사용하면 아래와 같이 f 함수를 바꿀 수 있고, 위 문제점도 해결된다. ``` void f() { unique_ptr pInv(createInvestment()); } ```

자원관리 객체

자원 관리에 객체를 사용하는 방법의 두가지 특징은 다음과 같다.

1.자원을 획득한 후에 자원 관리를 객체에 넘긴다.

스마트 포인터를 사용한 f 함수의 경우 creaetInvestment 로 자원을 획득한 후, pInv 객체를 초기화 하는데 사용한다.
이것을 RAII (Resource Acquisition is Initialization) 라고 부른다. (자원 획득 후 초기화)
자원을 획득하고 나서 바로 자원 관리 객체에 넘겨준다!

2. 자원 관리 객체는 자신의 소멸자를 사용해서 자원이 확실이 해제되도록 한다.

소멸자는 객체가 소멸될 때 자동적으로 호출되기 때문에, 블록을 떠나면 자원 해제가 제대로 이루어진다.
객체를 해제하다가 예외가 발생할 수는 있지만 이건 항목 8을 참고하자

shared_ptr

스마트 포인터를 복사하고 싶거나, 여러곳에서 참조하고 싶다면 shared_ptr을 쓰면 된다.
shared_ptr은 RCSP(reference-counting smart pointer) 이다.
RCSP 는 특정 어떤 자원을 가리키는 외부 객체의 개수를 유지하고 있다가, 그 개수가 0이 되면 해당 자원을 자동으로 삭제한다.
RCSP 의 동작은 가비지 컬렉션과 유사하다고 한다.

스마트 포인터의 소멸자

shared_ptr 은 소멸자 내부에서 delete 연산자를 사용한다.
동적으로 할당한 배열에 대해 shared_ptr을 쓰면 안된다는 얘기다.
컴파일 에러라도 나면 좋겠지만, 컴파일 에러도 안난다.

shared_ptr<string> sp(new string[10]);

c++ 표준 라이브러리에서도 동적 할당된 배열용으로 스마트 포인터 클래스를 제공하지 않는다.
why? 동적으로 할당된 배열은 vector 나 string 으로 대체 가능하기 때문!


정 shared_ptr 에 배열을 할당하고 싶으면, 삭제자를 따로 지정해줘야 한다. unique_ptr 은 배열 처리가 되어있다.

요약

  • 자원 누출을 막기 위해, 생성자 안에서 자원을 획득하고 소멸자에서 해제하는 RAII 객체를 사용하자
  • auto_ptr 말고 웬만하면 shared_ptr, unique_ptr 쓰자. auto_ptr은 복사되는 객체를 null 로 만든다.