c++로 코딩을 하다 보면 항상 주의해야 할 점이 있다. 바로 메모리 관리...
java, python 과 같은 언어들은 가비지 콜렉터가 있어서 메모리 해제를 언어 차원에서 해준다. 정말 편리하다. 하지만 c++ 에서는 개발자가 메모리를 사용하고 나면 반환을 신경써서 해줘야 한다. 그래서 생겨난 것이 smart pointer다. smart pointer를 잘 사용하면 c++에서도 java에서처럼 메모리 관리에 대한 부담을 상당히 낮출 수 있다.
smart pointer들은 라이브러리에 정의되어 있으며, 먼저 기본적인 smart_pointer의 원리를 가볍게나마 이해해두면 좋을 것 같다. smart_pointer 또한 class이고 내가 사용하고자 하는 resource pointer를 멤버로 참조한다. 그리고 smart_pointer의 destruction이 호출 될 때 resource pointer를 메모리에서 해제한다.
그럼 smart pointer의 3가지 unique_ptr, shared_ptr, weak_ptr를 정리해보자.
unique_ptr는 말 그대로 유일하다(?). 다시 말해, unique_ptr는 복사 될 수 없다. 아래 예제 코드를 보면 이해가 쉬울 것이다.
std::unique_ptr<int> ptr1 = std::unique_ptr<int>();
std::unique_ptr<int> ptr2 = ptr1; //error
ptr1을 unique_ptr로 생성하고 ptr2에 대입하면 에러가 발생할 것이다.
unique_ptr를 생성하는 방법은 위와 같이 std::unique_ptr()로 생성 할 수 있다. 또한 helper function으로 std::make_unique(parameters)도 가능하다.
std::unique_ptr<int> ptr1 = std::make_unique<int>();
unique_ptr의 특징으로 unique_ptr는 주로 로컬 스코프에서 사용된다.
기본적으로 unique_ptr와 같지만 내부적으로 reference counter를 가진다. resource pointer를 참조하는 shared_pointer의 수를 세고 이 counter가 0이되면 resource pointer를 메모리에서 해제한다.
std::shared_ptr<int> ptr1 = std::shared_ptr<int>();
std::shared_ptr<int> ptr2 = ptr1; // counter = 2
위에서 처럼 ptr1을 ptr2에 대입이 가능하다. ptr1을 ptr2에 대입하면 counter가 하나 증가한다.
std::shared_ptr<int> ptr1 = std::make_shared<int>();
shared_ptr 또한 helper function으로 make_shared가 있고 make류를 사용하면 new를 사용하지 않을 수 있다.
주의할 점으로, shared_ptr는 순환 참조가 생길 수 있다. A가 B를 참조하고 B가 A를 참조하면 순환 참조가 되어 counter가 줄어 들지 않고 메모리 누수가 발생한다. 이러한 점은 아래 정리할 weak_ptr로 해결 할 수 있다.
weak_ptr는 shared_ptr와 동일하다고 생각하면 된다. 하나 다른점은 weak_ptr는 counter를 증가시키지 않는다.
std::shared_ptr<int> ptr1 = std::shared_ptr<int>();
std::weak_ptr<int> ptr2 = ptr1;
순환 참조 문제를 해결하기 위해서는, A가 B를 weak_ptr로 참조하고 B가 A를 weak_ptr로 참조하면 해결 된다.
아래 링크는 smart_pointer를 이해하기 쉽게 정리한 글이여서 보면 좋을 것 같아 남긴다.
https://www.internalpointers.com/post/beginner-s-look-smart-pointers-modern-c