new/delete 사용법과 메모리 위험

Jaemyeong Lee·2024년 8월 28일

게임 서버1

목록 보기
50/220

이 Step에서 다루는 것

  • new/delete기본 패턴과 해제 후 처리
  • 가장 흔한 2대 사고
    • 메모리 누수(Memory Leak): new만 하고 delete를 안 함
    • Use-After-Free: delete한 객체를 다시 사용(해제 후 사용)
  • 사고의 뿌리 원인: 소유권(누가 delete 책임?)이 불명확할 때

학습 목표

  • “왜 delete p; p = nullptr;가 도움이 되는지”를 설명할 수 있다.
  • Use-After-Free가 “가끔은 안 터져서 더 위험”한 이유를 설명할 수 있다.
  • 포인터를 여러 곳에서 들고 있을 때, 어떤 설계가 필요한지(소유/비소유 분리)를 말할 수 있다.

기본 패턴(가장 단순한 1소유자 상황)

Monster* m = new Monster();
// ... 사용 ...
delete m;
m = nullptr; // 해제 후에는 바로 무효화(조기 발견에 도움)

핵심 규칙(실전용):

  • new를 했다면, 같은 책임 범위에서 반드시 delete가 나와야 합니다.
  • delete 뒤에는 포인터를 nullptr로 만들어 “실수로 다시 쓰는 버그”를 빨리 터뜨리는 게 좋습니다.

메모리 누수(Memory Leak): “delete 누락”

누수의 정체:

  • “더 이상 쓸 수 없는 메모리”를 프로그램이 계속 붙잡는 상태

전형적인 패턴:

  • 루프/이벤트에서 new만 하고 delete를 빼먹음
  • 시간이 지나면 프로세스 메모리가 점점 증가 → 결국 OOM/크래시
void SpawnMany()
{
    for (int i = 0; i < 1'000'000; ++i) {
        Monster* m = new Monster();
        // ... 어딘가에 저장하지도 않고 ...
        // delete m; // ❌ 빠지면 누수
    }
}

C# vs C++ 감각:

  • C#: GC가 “언젠가” 회수해줌 (편하지만 타이밍/비용 존재)
  • C++: 수동 관리(혹은 RAII) → 누수는 “나중에 크게 터지는 타입”이라 추적이 어렵습니다.

Use-After-Free: “삭제했는데도 주소가 남아있다”

중요한 착각:

  • delete p;는 “포인터 변수를 0으로 만드는 명령”이 아닙니다.
  • delete는 “p가 가리키는 대상 객체를 파괴”하는 것이고,
    포인터 값(주소)은 그대로 남아 댕글링 포인터(dangling pointer)가 됩니다.

그래서 p->hp 같은 접근은:

  • 크래시가 날 수도 있고
  • 안 날 수도 있습니다 (더 위험)
    → 둘 다 UB(정의되지 않은 동작) 입니다.

(핵심) 다중 포인터 시나리오: nullptr로 밀어도 안전하지 않은 이유

문제는 “한 포인터만 nullptr로 바꿔서는” 끝나지 않는다는 겁니다.

초기 상태:
  m1  ─────► [Monster 객체]
  player._target ─► (같은 Monster 객체)

delete m1; m1 = nullptr; 이후:
  m1  ─────► nullptr (안전)
  player._target ─► [이미 삭제된 자리]  (위험: dangling)

즉:

  • m1 = nullptr;m1만 안전하게 만들 뿐,
  • 같은 대상을 가리키던 다른 포인터(player._target)는 그대로 남아서 Use-After-Free가 됩니다.

해결 방향(설계 규칙)

  • 소유 포인터(삭제 책임 있음)는 “딱 한 군데”로 모읍니다.
  • 다른 곳은 비소유 포인터(관찰자)로 취급하고,
    소유자가 파괴할 때 관찰자들을 무효화하는 규칙이 필요합니다.

더 좋은 실전 해법은 이후 Step에서 배우는 스마트 포인터(RAII) 쪽에서 자연스럽게 정리됩니다.


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

  • delete p; 다음에 p가 자동으로 nullptr이 되지 않는 이유는?
  • Use-After-Free가 “가끔 안 터져서” 더 위험한 이유는?
  • 다중 포인터 상황에서 p = nullptr;만으로 충분하지 않은 이유는?

profile
李家네_공부방

0개의 댓글