본문 바로가기
DirectX/[팀플] Death's Door 모작

나름대로 큰? 규모의 소스코드에서의 shared_ptr 고찰

by GameStudy 2022. 11. 11.

0. 어제자로 프로젝트의 거의 모든 raw pointer가 shared_ptr로 교체되었음.

  이는 엔진 팀장님(선생님)이 모두 진행 하심.

 

1. Actor라는 부모 클래스를 상속 받는 Player 자식 클래스가 있었음.

 

2. Player 개체는 게임이 진행되는 와중에 단 한명이기에(서버 안 붙힌 게임이라)

  static 키워드를 통한 싱글톤 패턴으로 구현했음.

 

3. 강제로 Player 개체를 delete 하면 메모리 릭이 나지 않음.

  그리고 엔진 팀장님의 코드에서도 릭이 안남.

  근데 차이라면 상속 관계에 차이가 있음.

  엔진 팀장님의 Player 클래스는 Player -> EngineObject

  컨텐츠 팀원들의 Player 클래스는 Player -> Actor -> EngineObject

  

4. 다른 클래스를 만들어서 Actor 클래스를 상속 받는게 아닌

  EngineObject를 상속 받고, static 키워드를 통해 싱글톤 패턴으로 구현.

  Player와 똑같이 구현 및 코드 작성을 해보았으나, 역시 릭이 나지 않음.

 

5. 따라서 Actor 클래스의 멤버가 문제일거라는 합리적 의심을 함.

  최대한 엔진 코드와의 차이점을 나열하고 실험.

  엔진 코드 상의 Player 클래스에는 없고

  컨텐츠 코드 상의 Player 클래스에만 있는 멤버들을 집중적으로 탐구.

  5시간 동안 보았으나 그래도 못찾음.

  이렇게 해보니 확실한건, 엔진 코드 상의 Player와 똑같아지니까 릭 안남음...

 

6. 다시 원점으로 돌아와서 비교..

  결국 못찾음... 소기의 성과라면, raw pointer와 smart pointer를 섞어쓰지말자...

  근데 어떻게 엔진팀장님의 코드에선 싱글톤 개체를 delete 해주지 않았는데도

  소멸자가 알아서 호출되는 걸까..? 컨텐츠 팀원들의 싱글톤 개체처럼,

  delete를 안해줬다면 메모리 릭이 남는게 당연한거 아닐까라는 의문에 도달.

  그러나 이마저도, shared_ptr로 생성된 Player 개체라서 자동으로 소멸자가 호출됨을 

  토이 프로젝트로 확인함. 다만 new를 통해서 생성된 Player 개체라면, 소멸자 호출 안됨.

 

7. 소멸자가 호출되지 않으려면 어떻게 해야할까?

  누군가가 Player를 shared_ptr 멤버로 갖고 있고

  Player도 shared_ptr로 누군가를 갖고 있는 경우. 

  근데 여기서 한 번 더 꼬아서, 한 다리 건너서 서로의 shared_ptr을 멤버로 갖고 있다면?

 

8. 클라 팀원이 발견했다..!

  8.1 Player 개체가 싱글톤 패턴으로 구현되어 있는데, 따로 delete 안함.

    Player의 최상위 부모는 enable_shared_from_this<>를 상속 받고 있음.

  8.2 그와중에, 해당 개체를 shared_ptr로 구현하고, 싱글톤 패턴을 위한 

    static 멤버에는 shared_ptr의 raw pointer가 들어있음.

  8.3 Player FSM 관련 함수(특정 상태의 시작/중/끝 관련 call-back)에

    this를 넣어줄 필요가 있음. call-back 함수가 Player 클래스의 멤버 함수기 때문.

    이때 call-back 함수를 인자로 전달받는 매개변수의 자료형은 functional 자료형

  8.4 클라 팀원은 3번까지를 발견했고, 4번부터는 나만의 추측이다.

    위와 같은 상황에서, 아래와 같이 shared_from_this()를 전달하니

    매개변수에 복사 되며 shared_ptr(const shared_ptr<Player>& _Other)가 호출 되었던 것 같다.

    따라서 싱글톤 패턴으로 구현되어 있는 Player 개체의 raw pointer를 참조하는 

    shared_ptr이 하나 더 만들어 지게 됨.

  8.5 그런데, Player 클래스 내부에 GameEngineStateManager 클래스의

    StateManager 멤버를 값형으로 갖고 있음.

    그래서 위 그림에 . 연산자 사용

    GameEngineStateManager 클래스 내부에서도 모두 값형으로 멤버들을 갖고 있음.

  8.6 GameEngineState 클래스 내에는 아래와 같이 Call-back 함수들이 멤버로 존재함.

    이때, 이 Call-back 함수들에 인자로 전달된 shared_ptr이 어떤 방식으로든

    functional 내부에서 shared_ptr로 매개변수를 멤버로 저장하지 않았을까 라는 추측을 하게 됨.

 

9. 결론

  인자로 전달하는 shared_from_this() 혹은 shared_ptr을 하찮게 생각하지 말자.

'DirectX > [팀플] Death's Door 모작' 카테고리의 다른 글

Post Process Effects  (0) 2022.11.24
프로젝트 내의 셰이더 구조 고찰  (0) 2022.11.21
22-11-04  (0) 2022.11.04
22-11-02  (0) 2022.11.02
22-10-31  (0) 2022.11.01

댓글