본문 바로가기
개발 (언어)/C++

[Modern C++] weak_ptr, unique_ptr

by 진현개발일기 2024. 3. 31.

■ shared_ptr의 한계점

shared_ptr<Knight> k1(new Knight());
shared_ptr<Knight> k2(new Knight());
k1->_target = k2;
k2->_target = k1;

위와 같이 서로를 참조하고 있을 때 둘의 Ref Count는 각각 2가 된다.
애당초 ref count가 0이 되어야 메모리를 해제(delete)시키는데 k1이 소멸되어 ref count를 -1한다고 해도 ref count는 여전히 1이 되어버린다.

 

그러면 결국엔 서로를 추적하고 있는 상황에선 메모리 해제가 일어나지 않는 버그가 발생하므로 메모리 릭(Memory Leak)이 일어난다. 

 

▼ Ref Count가 0이 안되어서 소멸자가 호출이 안되는 상황



이런 상황에선 임의로 우리가 어떤 유저가 로그아웃을 해가지고 빠져나갔다면 k1->_target.reset(); 같은 코드를 넣어줌으로써 강제로 끊어줄 수 밖에 없다.


shared_ptr만 사용할 경우 위와 같은 문제가 발생할 수 있는데 weak_ptr까지 사용한다면 예방할 수가 있다.

■ weak_ptr

생명 주기에는 영향을 주지 않고 [ref count는 건드리지 않고] 경우에 따라 아직 날라가지 않았으면 다시 shared_ptr로 변환해서 사용할 수 있다.


독립적으로 사용할 수 없고 shared_ptr의 사이클 문제를 보충하기 위해 생겨난 shared-ptr 의존적인 포인터이다. 

 

아래와 같이 expired 되었는지 확인하고 접근이 가능하다면 lock()함수를 통해 shared_ptr로 캐스팅 후 사용할 수 있다.

 

[스마트 포인터 활용 두 가지 방법]

1. shared_ptr만 사용할거면 생명 사이클을 알아서 잘 관리해줘야 하고
2. shared_ptr, weak_ptr를 사용한다면 expired(), lock()같은 함수를 통해 실제로 메모리가 유효한지 체크 후 사용해줘야 한다.

[요약]

Shared_ptr은 레퍼런스 카운트라는 변수를 둬서 참조 개수를 관리하는 포인터이다. Shared_ptr로 다른 애를 복사할 때마다 즉, 참조 횟수가 늘어날 때마다 레퍼런스 카운터를 늘려준다. 그리고 이러한 레퍼런스 카운트는 본인을 참조하는 개체가 참조를 해제 할 때마다 -1씩 줄어드는데 0이 될때만 개체의 메모리를 실제로 해제하는 방식이다.


Weak_ptr가 나온 이유는 서로 참조하는 사이클이 발생하면 shared_ptr이 서로를 절대 놔주지 않는 문제가 발생하기 때문에 [Deadlock 같은 상황] 그걸 보완하기 위한 용도로 weak pointer가 등장했다.

 

weak pointer는 레퍼런스 카운트 자체에는 영향을 주지 않지만 Weak Count라는 것을 별도로 관리하고 있고 그 레퍼런스 블록 자체를 weak pointer가 들고 있기 때문에 이를 이용하여 어떤 메모리가 날라갔는지 여부를 확인할 수 있다.

 

그 여부를 확인한 다음에 안전하게 접근을 하거나 날라갔으면 그냥 버리는 식으로 둘 중 하나를 선택할 수 있도록 도와주는 것이 weak pointer다.

 

▼ weak_ptr를 사용함으로써 소멸자가 호출되도록 설계한 결과

■ unique_ptr

복사 관련 함수(ex. 복사 생성, 복사 대입.. etc) 가 다 막혀있는 포인터. 
하지만 유일하게 내용을 덮어씌우는 방식은 std::move를 통해 이동연산자를 호출하는 것이다.

 

728x90