C++에서 동적 메모리 관리: 포인터와 삭제의 올바른 사용

포인터가 항상 delete를 필요로 하는 것은 아니다. 메모리 해제 여부는 해당 포인터가 가리키는 데이터의 생성 위치수명 주기에 따라 결정된다.

힙(동적 메모리)에서 new 또는 new[]로 할당된 메모리는 반드시 delete 또는 delete[]로 수동으로 해제해야 하며, 그렇지 않으면 메모리 누수가 발생한다. 반면 스택, 정적 영역, 글로벌 영역 등에서 생성된 메모리는 시스템이 자동으로 관리하므로, 이들에 대해 delete를 호출하면 정의되지 않은 동작(보통 프로그램 크래시)을 유발한다.

1. delete가 필요한 경우

new로 힙에 할당한 메모리는 반드시 사용 후 해제해야 한다.

int* handle = new int{42};     // 힙에 정수 하나 할당, 초기값 42
// ... 작업 수행
delete handle;                 // 메모리 반환
handle = nullptr;              // 안전성 확보: 둔탁 포인터 방지

배열 형태의 동적 할당은 다음과 같이 처리한다:

int* buffer = new int[50];     // 50개의 정수 배열 할당
// ... 사용
delete[] buffer;               // 배열 전체 해제

2. delete가 불필요하거나 금지되는 경우

  • 스택 변수를 가리키는 포인터: 스택 메모리는 함수 종료 시 자동으로 회수되며, 수동 해제할 수 없다.
int value = 30;
int* p = &value;               // 스택 상의 변수 참조
// delete p;                   // 오류! 스택 메모리에 대해 삭제 불가
  • 정적 또는 전역 변수를 가리키는 포인터: 정적 변수는 프로그램 시작 시 할당되고 종료 시 자동 소멸되며, delete로 해제할 수 없다.
static double global_data = 1.5;
double* ptr = &global_data;    // 정적 영역 접근
// delete ptr;                  // 위험! 정적 메모리에 대한 삭제는 금지
  • 문자열 리터럴: 읽기 전용 데이터 영역에 저장되며 수정도, 해제도 불가능하다.
const char* message = "안녕하세요";   // 읽기 전용 영역
// delete message;                    // 잘못된 사용
  • 스마트 포인터 사용 시: 현대 C++에서는 std::unique_ptr, std::shared_ptr 등의 스마트 포인터를 활용해 메모리 관리를 자동화할 수 있다. 수동 delete가 필요 없다.
#include <memory>
std::unique_ptr<int> resource = std::make_unique<int>(100);
// 범위를 벗어나면 자동으로 해제됨 → 수동 삭제 불필요

3. 왜 무분별한 delete는 위험한가?

  • 비힙 영역에 대해 delete를 호출하면 메모리 관리 체계가 파괴되어 프로그램이 비정상 종료될 수 있다.
  • 동일한 포인터에 대해 여러 번 delete를 호출하는 것도 위험하며, 이미 해제된 포인터에 대해 다시 호출하면 불정형 행동이 발생한다. 단, nullptr에 대해 delete는 안전하다.

4. 결론

  • 단지 new로 생성된 메모리만이 delete를 필요로 한다.
  • 포인터 자체는 단순한 주소일 뿐이며, 해제 여부는 그 주소가 가리키는 메모리의 출처에 달려 있다.
  • 메모리 관리는 "누가 할당했는지, 그 책임을 누구에게 두는가"의 원칙을 따르고, 가능한 한 스마트 포인터를 사용하여 수동 관리를 피해야 한다.

따라서, 모든 포인터에 대해 delete를 적용하는 것은 오류이며, 동적으로 할당된 힙 메모리에만 수동 해제가 필요하다.

태그: C++ 메모리 관리 포인터 스마트 포인터 동적 할당

7월 31일 15:21에 게시됨