2014-01-24

RAII는 무엇인가

 RAII는 C++에서 자주 쓰이는 idiom으로 자원의 안전한 사용을 위해 객체가 쓰이는 스코프를 벗어나면 자원을 해제해주는 기법이다. C++에서 heap에 할당된 자원은 명시적으로 해제하지 않으면 해제되지 않지만, stack에 할당된 자원은 자신의 scope가 끝나면 메모리가 해제되며 destructor가 불린다는 원리를 이용한 것이다.
 원래는 exception 등으로 control flow가 예상치 못하게 변경될 때를 대비하기 위해서 쓰이던 기법이다.

 아래의 예제를 보자.

 첫 번째 코드는 위험하다. thisFunctionCanThrowException() 함수에서 exception을 발생시킨다면 resource를 해제하지 못한다.
두 번째 코드는 일단은 resource를 해제하고 있다. 하지만 보기에 좋은 코드는 아니다. 그리고 유지하기도 어려워진다.
세 번째 코드는 RAII를 위해 c++ 11의 스마트 포인터인 unique_ptr을 이용하는 방법이다. unique_ptr은 소멸할 때 자신이 들고 있는 메모리를 해제시켜주기 때문에 함수 밖으로 나가면 resource가 해제되는 것이 보장된다.

 여기서 말하는 자원은 단순히 heap 메모리만을 말하는 것이 아니다. heap 메모리 이외에도 파일이나 db와 같은 것들도 전부 RAII를 이용해 안전하게 사용할 수 있다.
여기에 더 나아가서 특정 scope를 벗어나면 반드시 실행돼야 하는 코드들도 RAII를 이용해 처리할 수 있다. 즉, 다른 언어에서 finally에 해당하는 구문을 RAII를 이용해서 처리할 수 있다. 실제로 C++의 아버지이자 RAII라는 용어를 처음 만든 Bjarne Stroustrub는 c++에 finally를 집어넣지 않는 이유를 "RAII가 있는데 굳이 있을 필요가 없다."라고 말하고 있다.

댓글 3개:

  1. 예제 2번째인 unmaintanableFunction 에 문제가 있습니다.

    new Resource() 내부에서 exception 이 발생하는 경우
    초기화되지 않은 resource 포인터를 삭제할 때, 메모리 오류가발생합니다.
    null 로 초기화해야 하며, 동시에 삭제시에도 null 검사가 있어야 합니다.

    추가적으로 인스턴스의 메모리할당만 못하는 경우,
    컴파일러의 구 버전이나 설정에 따라, exception 이 발생하지 않고,
    null만 return 되는 경우가 있는데, 예제 2,3번이 모두 해당됩니다.

    답글삭제
    답글
    1. 한동안 바빠서 블로그를 방치해놓고 있다 보니 이제야 봤네요. 답변 감사합니다.
      말씀하신 대로 c++의 new operator는 예외를 발생할 수 있으니 14번째 줄에서 null 포인터로 초기화해야 합니다.
      null 포인터로 초기화하도록 수정했습니다.
      하지만 삭제할 때 null 검사를 할 필요는 없습니다. c++은 null 포인터를 삭제할 때 아무것도 하지 않는 것이 보장됩니다.

      삭제