KoreanFoodie's Study

[C++ 게임 서버] 2-2. 스마트 포인터 본문

Game Dev/Game Server

[C++ 게임 서버] 2-2. 스마트 포인터

GoldGiver 2023. 8. 23. 20:37

Rookiss 님의 '[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버' 를 들으며 배운 내용을 정리하고 있습니다. 관심이 있으신 분은 꼭 한 번 들어보시기를 추천합니다!

[C++ 게임 서버] 2-2. 스마트 포인터

핵심 :

1. 스마트 포인터에는 3종류가 있다. unique_ptr, shared_ptr, weak_ptr. 이 중, unique_ptr 는 소유권 개념을 위한 간단하고 가벼운 스마트 포인터이다.

2. shared_ptr 를 생성하면 사용하고자 하는 타입에 대한 메모리와 참조 카운트 등을 관리하는 제어 블록(Control Block)에 대한 메모리가 둘 다 할당된다. shared_ptr 의 경우 순환 문제가 발생할 수 있으므로, 주의해야 한다! 

3. shared_ptr 에 대한 참조 횟수가 사라지면 우리가 사용하고자 하는 데이터에 대한 메모리는 해제되나, 만약 해당 포인터를 참조하는 weak_ptr 가 있을 경우, 제어 블록은 해당 포인터를 참조하는 weak_ptr 까지 전부 사라져야 메모리가 해제된다.

음.. 사실 스마트 포인터에 대한 얘기는 정말 지겹도록(?) 많이 해 왔으므로, 여기에서 더 추가할 내용은 거의 없기는 하다.

다만 위의 '핵심' 에 적힌 내용을 대략적으로 복습한다고 생각하고 넘어가자. 😅

 

우리가 평소에 매우 유용하게 사용하는 shared_ptr 의 경우, cycle 이 생겨서 메모리가 해제되지 않는 상황을 만들지 않도록 주의해야 한다. 예를 들어, 다음과 같은 상황이다.

class Parent
{
  shared_ptr<Child> child;
}

class Child
{
  shared_ptr<Parent> parent;
}

위처럼 클래스가 서로의 shared_ptr 객체를 들고 있을 경우, 자칫 메모리 해제가 안 될 수도 있다.

만약 서로가 서로를 어쩔 수 없이 참조해야 한다면.. 한쪽은 weak_ptr 를 들고 있도록 하자! 😉

class Parent
{
  shared_ptr<Child> child;
}

class Child
{
  weak_ptr<Parent> parent;
}

 

아, 그리고 shared_ptr 에 대한 참조 횟수가 사라지면 우리가 사용하고자 하는 데이터에 대한 메모리는 해제되나, 만약 해당 포인터를 참조하는 weak_ptr 가 있을 경우, 제어 블록은 해당 포인터를 참조하는 weak_ptr 까지 전부 사라져야 메모리가 해제된다.

 

진짜일까? 증거로, 소멸자 쪽 코드를 제시하겠다 😂

// shared_ptr 의 소멸자. _Decref() 를 호출한다
~shared_ptr() noexcept { // release resource
    this->_Decref();
}

// _Decref() 는 use count 인 _Uses 를 체크한 후,
// _Destroy() 와 _Decwref() 를 호출한다
void _Decref() noexcept { // decrement use count
    if (_MT_DECR(_Uses) == 0) {
        _Destroy();
        _Decwref();
    }
}

// 그런데 _Delete_this() 가 호출되려면... Weak 카운트가 0 이어야 한다!
void _Decwref() noexcept { // decrement weak reference count
    if (_MT_DECR(_Weaks) == 0) {
        _Delete_this();
    }
}

// 어라, 근데 _Destroy() 는 데이터만 해제한다. 
// 제어블록까지 해제하려면 _Delete_this() 가 호출되어야 한다!
virtual void _Destroy() noexcept     = 0; // destroy managed resource
virtual void _Delete_this() noexcept = 0; // destroy self

위의 코드를 보면, Weak Pointer 의 참조 횟수인 _Weaks 가 0 이어야 _Delete_this() 가 호출되어 제어블록이 해제 되는 것을 확인할 수 있다! 😊

 

그리고... weak_ptr 는 소멸할 때, _Decwref() 를 호출해 준다! 😮

~weak_ptr() noexcept {
    this->_Decwref();
}

 

아, 스마트 포인터에 대해 이전에 정리해 둔 포스팅이 있으니, 더 많은 정보를 얻고 싶으면 이 글을 참고하자 😉

 

자매품으로, 언리얼 스마트 포인터에 대해 정리한 글 1, 글 2도 있다 😆

Comments