동적 메모리 할당
2025. 3. 28. 08:43ㆍProgramming Languages/C++
6.3 동적 메모리 할당
6.3.1 정적 vs 동적 메모리
C++에서 메모리는 크게 두 가지 방식으로 할당됩니다:
- 정적 메모리 할당:
- 컴파일 시간에 크기가 결정됩니다.
- 스택 메모리 또는 전역/정적 메모리에 저장됩니다.
- 변수 수명이 정해져 있습니다(블록이 끝나거나 프로그램이 종료될 때).
int num = 42; // 스택 메모리에 할당 static int counter = 0; // 정적 메모리에 할당
- 동적 메모리 할당:
- 실행 시간에 크기가 결정됩니다.
- 힙 메모리에 저장됩니다.
- 명시적으로 해제할 때까지 메모리가 유지됩니다.
int* dynamicNum = new int; // 힙 메모리에 할당 *dynamicNum = 42; delete dynamicNum; // 할당된 메모리 해제
6.3.2 new와 delete 연산자
C++에서는 new와 delete 연산자를 사용하여 힙 메모리를 동적으로 할당하고 해제합니다:
#include <iostream>
int main() {
// 단일 변수 동적 할당
int* ptr = new int; // 메모리 할당
*ptr = 42; // 값 설정
std::cout << "동적 할당된 정수: " << *ptr << std::endl;
delete ptr; // 메모리 해제
// 초기화와 함께 할당
double* dPtr = new double(3.14159);
std::cout << "동적 할당된 실수: " << *dPtr << std::endl;
delete dPtr;
// 배열 동적 할당
int size = 5;
int* arr = new int[size]; // 크기 5인 정수 배열 할당
// 배열 초기화
for (int i = 0; i < size; i++) {
arr[i] = i * 10;
}
// 배열 출력
for (int i = 0; i < size; i++) {
std::cout << "arr[" << i << "] = " << arr[i] << std::endl;
}
delete[] arr; // 배열 메모리 해제 ([] 표기 중요!)
return 0;
}
주의사항:
- new로 할당한 메모리는 반드시 delete로 해제해야 합니다.
- new[]로 할당한 배열은 반드시 delete[]로 해제해야 합니다.
- 이미 해제된 메모리를 다시 해제하면 정의되지 않은 동작이 발생합니다.
- 메모리 누수(memory leak)를 방지하기 위해 모든 동적 할당 메모리를 적절히 해제해야 합니다.
6.3.3 동적 메모리 할당 오류 처리
C++에서 메모리 할당 실패 시 예외 처리 방법:
#include <iostream>
#include <new> // std::bad_alloc 예외 클래스
#include <cstdlib> // std::exit 함수
int main() {
try {
// 매우 큰 메모리 블록 할당 시도
int* hugeArray = new int[1000000000000]; // 가능성 있는 메모리 할당 실패
// 이 코드는 메모리 할당이 성공하면 실행됨
std::cout << "메모리 할당 성공" << std::endl;
delete[] hugeArray;
}
catch (const std::bad_alloc& e) {
// 메모리 할당 실패 시 예외 처리
std::cerr << "메모리 할당 실패: " << e.what() << std::endl;
}
// 예외를 던지지 않는 버전 (C++11)
int* ptr = new(std::nothrow) int[1000000000];
if (ptr == nullptr) {
std::cerr << "메모리 할당 실패 (nothrow)" << std::endl;
} else {
std::cout << "메모리 할당 성공 (nothrow)" << std::endl;
delete[] ptr;
}
return 0;
}
6.3.4 메모리 누수와 스마트 포인터 소개
메모리 누수는 할당된 메모리를 해제하지 않을 때 발생하는 문제입니다:
void memoryLeakExample() {
int* ptr = new int(42);
// delete ptr; 가 없으면 메모리 누수 발생
} // 함수가 종료되면 ptr은 소멸되지만, 할당된 메모리는 그대로 남음
C++11부터는 스마트 포인터를 사용하여 메모리 관리를 자동화할 수 있습니다:
#include <iostream>
#include <memory> // 스마트 포인터
int main() {
// 고유 소유권 스마트 포인터
std::unique_ptr<int> uptr = std::make_unique<int>(42); // C++14
// std::unique_ptr<int> uptr(new int(42)); // C++11
std::cout << "unique_ptr 값: " << *uptr << std::endl;
// 함수 종료 시 자동으로 메모리 해제
// 공유 소유권 스마트 포인터
std::shared_ptr<int> sptr1 = std::make_shared<int>(100);
std::cout << "shared_ptr 값: " << *sptr1 << std::endl;
{
std::shared_ptr<int> sptr2 = sptr1; // 참조 카운트 증가
std::cout << "참조 카운트: " << sptr1.use_count() << std::endl; // 2
// sptr2 값 변경 시 sptr1도 영향 받음(같은 객체 가리킴)
*sptr2 = 200;
std::cout << "sptr1 값: " << *sptr1 << std::endl; // 200
} // 블록 종료 시 sptr2 소멸, 참조 카운트 감소
std::cout << "블록 이후 참조 카운트: " << sptr1.use_count() << std::endl; // 1
// weak_ptr: 소유권 없이 shared_ptr 관찰
std::weak_ptr<int> wptr = sptr1;
if (auto temp = wptr.lock()) { // shared_ptr 획득 시도
std::cout << "weak_ptr를 통한 값 접근: " << *temp << std::endl;
} else {
std::cout << "weak_ptr가 가리키는 객체 소멸됨" << std::endl;
}
return 0;
} // sptr1 소멸, 메모리 자동 해제
스마트 포인터의 장점:
- RAII(Resource Acquisition Is Initialization) 패턴을 통한 자동 메모리 관리
- 예외 안전성 보장
- 소유권 모델을 명시적으로 표현
- 교차 참조 문제 해결(shared_ptr과 weak_ptr 조합)
'Programming Languages > C++' 카테고리의 다른 글
고급 포인터 주제 (0) | 2025.03.28 |
---|---|
참조 (0) | 2025.03.28 |
포인터의 기본 (0) | 2025.03.28 |
포인터와 참조 (0) | 2025.03.28 |
챕터5. 실습 문제 (0) | 2025.03.28 |