전에 포스팅한 RefCounting과 스마트 포인터는 RefCounting을 상속받는 클래스에만 사용할 수 있고, 순환 문제를 해결할 수 없다.
SmartPointer에는 총 3종류가 있는데, 각각 unique_ptr, shared_ptr, weak_ptr이 있다.
unique_ptr는 복사생성자, 복사연산자 등 복사연산이 delete되어 있는 포인터라고 생각하면 된다. (이동연산자는 가능)
1. RefCountBlock
shared_ptr는 ptr가 가리키는 객체와 RefBlock이라는 RefCount를 관리하는 메모리가 같이 할당된다.
RefCount는 ptr을 가리키는 shared_ptr의 개수만큼 늘어나고, RefCount가 0이되면 ptr가 가리키는 객체를 delete로 소멸시킨다.
RefCountBlock에서 관리하는 변수는 Uses와 Weaks 가 있는데, Uses는 shared_ptr가 가리키는 ptr의 개수이고, Weaks는 weak_ptr에 사용되는 변수로, Uses가 0이 되면 ptr의 메모리가 delete가 되지만, RefCountBlock은 해제되지 않는데, RefCountBlock의 해제에 관여되는 변수가 Weaks로 Weaks가 0이되면 RefCountBlock을 해제한다.
2. Cycle Problem
위에 서술한 weak_ptr가 순환 문제를 해결할 수 있는데, 순환 문제란 shared_ptr들이 가리키는 객체들이 가리키는 객체들을 다시 가리켜 cycle이 발생하면 메모리를 해제시키지 못하는 문제가 발생하는데, 그 문제를 순환 문제라고 한다.
예를 들어서 ptr1내부에서 ptr2를 가리키는 변수가 있다고 하고, 반대로 ptr2의 내부에 ptr1을 가리키는 변수가 있다고 한다면, ptr1의 Uses는 2, ptr2의 Uses도 2일 것이다. ptr1이 nullptr로 제거된다면, ptr1의 Uses는 1이고, ptr2의 Uses는 2가 될 것이다. 마찬가지로 ptr2가 nullptr로 제거되면 ptr1과 ptr2가 각각 Uses가 1로 메모리가 삭제되지 않게 된다.
weak_ptr은 ptr가 가리키는 객체의 삭제를 관여하지 않고 RefCountBlock만 관여하는데, 예를 들어서 위와 같은 상황에서 ptr1 내부에서 ptr2를 가리키는 포인터를 shared_ptr 대신에 weak_ptr을 사용한다면, ptr1의 Uses는 1이고, weak값이 2가 된다. Uses값이 0이되면 ptr가 가리키는 객체를 delete하고, Uses값과 weak값이 같이 0이되면 RefCountBlock을 delete한다. weak_ptr은 shared_ptr로 변환가능한대 .lock()함수를 사용하면 된다.
lock함수가 nullptr값을 반환하면 이미 객체가 delete되있고, RefCountBlock만 남은상태이다.
std::make_shared<>();를 사용하면 새로운 obj2를 만들어서 ptr의 클래스와 RefCountBlock을 합쳐서 하나의 클래스로 만들어서 shared_ptr에 저장하기때문에, 더 효율적이다.
A* a = new A();
shared_ptr<A> p1(a);
shared_ptr<A>p2(a); 이렇게 만들면 각각 shared_ptr을 처음 만든 것 처럼 생성되서 p1의 refCountBlock도 따로만들고, p2의 refCountBlock도 따로 생성해서 각각 Uses가 1이 되는 참사가 벌어질 수 있다. 그럼으로 shared_ptr을 초기화할 때, 주소값을 넣는 대신에 std::makre_shared<>()를 사용하는게 좋다.
'MMOServer' 카테고리의 다른 글
SList(C++) 불완전 (0) | 2025.03.28 |
---|---|
메모리 관련 (0) | 2025.03.27 |
RefCount(C++) (0) | 2025.03.21 |
DeadLock 탐지(C++) (0) | 2025.03.20 |
Read-Write Lock(C++) (0) | 2025.03.19 |