생명 주기와 메모리 관리 책임

Jaemyeong Lee·2024년 10월 30일

게임 서버1

목록 보기
60/220

이 Step에서 다루는 것

  • 메모리 버그의 핵심 원인: “누가 delete할지”가 불명확한 설계
  • 소유권(ownership) 규칙을 코드/함수 계약으로 고정하는 방법
  • dangling pointer 시나리오를 구조적으로 줄이는 방법
  • unique_ptr/shared_ptr/weak_ptr 선택 기준

학습 목표

  • 함수 경계에서 소유권 이동을 명확히 설계할 수 있다.
  • 한 객체를 여러 곳이 참조할 때 dangling을 줄이는 규칙을 설명할 수 있다.
  • “기본은 unique_ptr, 공유가 필요할 때만 shared_ptr” 원칙을 적용할 수 있다.

누가 delete하는가: 소유권을 먼저 정하자

실패하는 코드의 공통점은 같습니다.

  • 생성한 쪽/사용한 쪽/보관한 쪽이 서로 “상대가 지우겠지”라고 생각함

그래서 규칙을 먼저 정합니다.

  • 소유자(owner)는 정확히 1곳
  • 소유자만 delete (또는 소멸 시 자동 해제)
  • 비소유자(observer)는 참조만 하고 절대 delete하지 않음

raw 포인터 계약 예시:

Item* item = DropItemRaw(); // 호출자가 우선 소유

if (inventory.AddItem(item)) {
    item = nullptr;          // 소유권을 인벤토리로 넘김
} else {
    delete item;             // 추가 실패 시 호출자가 정리
    item = nullptr;
}

핵심:

  • API 이름/문서/주석에 “소유권 이동 여부”를 명시해야 사고가 줄어듭니다.

실전 시나리오: 추적 대상 삭제와 dangling

예: 미사일이 Mutalisk* target을 들고 추적 중일 때

  • 타겟이 먼저 죽어서 삭제되면
  • 미사일의 target은 댕글링 포인터가 됩니다.

대응 전략:

  • 대상 소멸 시 추적자들에게 “대상 무효화”를 알리는 구조(관찰자 해제)
  • 또는 포인터 대신 id를 저장하고 매 프레임 안전 조회
  • 또는 weak_ptr를 사용해 “존재할 때만 lock해서 사용”

중요:

  • “삭제 시 관련 포인터 전부를 갱신”은 규칙이 아니라 필수 운영 절차입니다.

스마트 포인터로 책임을 타입에 담기

  • std::unique_ptr<T>: 단일 소유(기본 선택), 참조 카운트 없음
  • std::shared_ptr<T>: 공유 소유(참조 카운트), 정말 공유가 필요할 때만
  • std::weak_ptr<T>: 비소유 관찰자, dangling/순환 참조 완화
#include <memory>

std::unique_ptr<Item> DropItem()
{
    return std::make_unique<Weapon>();
}

실전 원칙(짧게):

  • 기본: unique_ptr
  • 공유 소유가 “정말 필요할 때만” shared_ptr
  • 관찰자 참조에는 weak_ptr 고려

체크 질문 (스스로 답해보기)

  • AddItem 실패 시 누가 메모리를 해제해야 하는가?
  • dangling pointer를 줄이려면 “삭제 시점”에 어떤 규칙이 필요할까?
  • 왜 기본 선택을 shared_ptr이 아니라 unique_ptr로 두는가?

profile
李家네_공부방

0개의 댓글