핵심 정리
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[] 사용현대 C++에서는 이 카드를 "어떻게 new/delete를 잘 쓰나"보다 "왜 이걸 직접 안 쓰는 쪽으로 가는가" 관점으로 읽는 편이 더 맞습니다.
문법
먼저 다시 보는 기본형은 아래입니다.
- 단일 객체 할당:
new T,delete - 배열 할당:
new T[n],delete[] - 실패 처리:
bad_alloc,nothrow - 대안:
unique_ptr,vector,string
어떤 메모리 관리 방식부터 고르면 되나
| 상황 | 먼저 떠올릴 것 |
|---|---|
| 단일 객체 소유 | std::unique_ptr |
| 가변 길이 배열 | std::vector |
| 문자열 버퍼 | std::string |
| 공유 소유가 정말 필요함 | std::shared_ptr |
| 저수준 API와 직접 맞물림 | 마지막에만 new / 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 을 써야 함cpp
// ❌ 조기 반환 경로에서 delete 누락
Widget* w = new Widget();
if (!isReady()) return; // 누수
delete w;
// ✅ RAII로 조기 반환에도 안전
auto w = std::make_unique<Widget>();
if (!isReady()) return;현대 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,delete - 배열이면
delete[] - 실패는 기본적으로
bad_alloc - 새 코드는
make_unique,make_shared,vector,string우선 - 수동 메모리 관리는 마지막 선택지
주의할 점
new로 할당하면 반드시 delete로 해제해야 합니다. 예외가 발생하거나 조기 반환하는 경우 delete를 빠뜨리기 쉽습니다. 새 코드에서는 std::make_unique / std::make_shared를 사용하면 이 문제를 원천 차단할 수 있습니다.
배열 new[]에는 반드시 delete[]를 써야 합니다. 단순 delete를 쓰면 정의되지 않은 동작입니다.
cpp
// ❌ 동적 배열이 필요해도 직접 new[]를 먼저 꺼내지 않는다
int* scores = new int[n];
// ✅ 크기 가변 배열이면 vector가 기본
std::vector<int> scores_ok(n);참고 링크
1 sources