C++메모리와 자원 관리

동적 메모리 기본

`new`와 `delete`로 힙 메모리를 수동 관리하는 방법과 그 위험성, 그리고 스마트 포인터로 전환해야 하는 이유를 정리합니다.

마지막 수정 2026년 3월 26일

기본 패턴

cpp
// 힙에 할당
int* p = new int{42};
std::cout << *p;    // 42

// 반드시 해제
delete p;
p = nullptr;        // 댕글링 포인터 방지

// 배열
int* arr = new int[5]{1, 2, 3, 4, 5};
delete[] arr;       // 배열은 delete[] 사용

설명

스택 vs 힙

cpp
// 스택 — 함수 종료 시 자동 해제, 크기 제한 있음
int local = 42;

// 힙 — 프로그래머가 직접 수명 관리, 크기 제한 없음
int* heap = new int{42};
// ... 사용 ...
delete heap;

new 실패 처리

메모리가 부족하면 new는 기본적으로 std::bad_alloc 예외를 던집니다.

cpp
#include <new>

try {
    int* p = new int[1000000000];
    delete[] p;
} catch (const std::bad_alloc& e) {
    std::cerr << "할당 실패: " << e.what();
}

// 예외 대신 nullptr 반환 원할 때
int* p = new(std::nothrow) int[1000000000];
if (!p) { /* 할당 실패 처리 */ }

흔한 메모리 버그

cpp
// 1. 메모리 누수 — delete를 빠뜨림
int* p = new int{10};
// delete p; 를 빠뜨리면 힙에 계속 남아 있음

// 2. 댕글링 포인터 — 해제 후 접근
delete p;
*p = 20;    // ❌ 정의되지 않은 동작 (UB)

// 3. 이중 해제 — delete를 두 번 호출
delete p;
delete p;   // ❌ UB

// 4. 배열 mismatch
int* arr = new int[5];
delete arr; // ❌ delete[] arr 을 써야 함

현대 C++ — 스마트 포인터 사용 권장

new/delete를 직접 쓰면 위 실수가 발생하기 쉽습니다. 현대 C++에서는 스마트 포인터를 사용해 자동으로 메모리를 해제합니다.

cpp
#include <memory>

// unique_ptr — 단독 소유, 범위를 벗어나면 자동 해제
auto p = std::make_unique<int>(42);
std::cout << *p;
// delete 불필요 — 소멸 시 자동 해제

// shared_ptr — 공유 소유, 마지막 소유자가 해제
auto sp1 = std::make_shared<int>(100);
auto sp2 = sp1;  // 참조 카운트 2
// sp1, sp2 모두 범위를 벗어날 때 해제됨

빠른 정리

항목설명
new T힙에 T 객체 할당, 초기화 후 포인터 반환
delete p단일 객체 해제
new T[n]힙에 배열 할당
delete[] p배열 해제 (delete와 혼용 금지)
std::bad_alloc메모리 부족 시 던지는 예외
make_unique<T>unique_ptr 생성 (단독 소유, 권장)
make_shared<T>shared_ptr 생성 (공유 소유)

주의할 점

new로 할당하면 반드시 delete로 해제해야 합니다. 예외가 발생하거나 조기 반환하는 경우 delete를 빠뜨리기 쉽습니다. 새 코드에서는 std::make_unique / std::make_shared를 사용하면 이 문제를 원천 차단할 수 있습니다.

배열 new[]에는 반드시 delete[]를 써야 합니다. 단순 delete를 쓰면 정의되지 않은 동작입니다.

참고 링크

1 sources