Topic 2: 동적 메모리 할당 🧠
🎯 학습 목표
- 동적 메모리 할당의 개념과 필요성을 이해할 수 있다
new
와delete
연산자를 올바르게 사용할 수 있다- 메모리 누수를 방지하는 방법을 익힐 수 있다
- 배열과 객체의 동적 할당을 구현할 수 있다
📖 동적 메모리 할당이란?
동적 메모리 할당은 프로그램 실행 중에 필요한 만큼의 메모리를 할당하고 해제하는 기능입니다.
아날로지: 호텔 예약과 같습니다.
- 정적 할당: 미리 방을 예약해두는 것 (배열 크기 고정)
- 동적 할당: 필요할 때 방을 예약하고, 떠날 때 반납하는 것
🔍 정적 vs 동적 메모리
#include <iostream>
using namespace std;
int main() {
// 정적 메모리 할당 (스택)
int staticArray[100]; // 컴파일 시 크기 결정
// 동적 메모리 할당 (힙)
int size;
cout << "배열 크기를 입력하세요: ";
cin >> size;
int* dynamicArray = new int[size]; // 실행 시 크기 결정
// 메모리 해제
delete[] dynamicArray;
return 0;
}
🔧 new와 delete 연산자
기본 사용법
#include <iostream>
#include <string>
using namespace std;
int main() {
// 1. 단일 변수 동적 할당
int* ptr = new int(42); // 초기값 42로 할당
cout << "값: " << *ptr << endl; // 출력: 42
delete ptr; // 메모리 해제
// 2. 배열 동적 할당
int size = 5;
int* arr = new int[size];
// 배열 초기화
for(int i = 0; i < size; i++) {
arr[i] = i * 10;
}
// 배열 출력
for(int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
delete[] arr; // 배열 메모리 해제 (주의: delete[])
return 0;
}
클래스 객체 동적 할당
#include <iostream>
#include <string>
using namespace std;
class Student {
private:
string name;
int age;
public:
Student(string n = "Unknown", int a = 0) : name(n), age(a) {
cout << "생성자 호출: " << name << endl;
}
~Student() {
cout << "소멸자 호출: " << name << endl;
}
void display() {
cout << "이름: " << name << ", 나이: " << age << endl;
}
};
int main() {
// 단일 객체 동적 할당
Student* student1 = new Student("김철수", 20);
student1->display();
delete student1; // 소멸자 자동 호출
cout << "---" << endl;
// 객체 배열 동적 할당
int count = 3;
Student* students = new Student[count]{
Student("이영희", 21),
Student("박민수", 22),
Student("최지원", 19)
};
for(int i = 0; i < count; i++) {
students[i].display();
}
delete[] students; // 모든 객체의 소멸자 호출
return 0;
}
⚠️ 메모리 누수와 예방
메모리 누수 예시
#include <iostream>
using namespace std;
void memoryLeak() {
int* ptr = new int[1000]; // 메모리 할당
// ... 작업 수행
if(true) { // 조건부 return
return; // delete[] ptr 호출 안 됨 → 메모리 누수!
}
delete[] ptr; // 실행되지 않음
}
void properMemoryManagement() {
int* ptr = new int[1000];
try {
// ... 작업 수행 (예외 발생 가능)
// 예외가 발생해도 메모리 해제를 보장
delete[] ptr;
}
catch(...) {
delete[] ptr; // 예외 발생 시에도 메모리 해제
throw; // 예외 재발생
}
}
RAII 패턴 (Resource Acquisition Is Initialization)
#include <iostream>
#include <memory> // 스마트 포인터
using namespace std;
class SafeArray {
private:
int* data;
int size;
public:
SafeArray(int s) : size(s) {
data = new int[size];
cout << "배열 할당: " << size << "개 요소" << endl;
}
~SafeArray() {
delete[] data;
cout << "배열 해제" << endl;
}
int& operator[](int index) {
return data[index];
}
int getSize() const { return size; }
};
int main() {
{ // 스코프 시작
SafeArray arr(5);
for(int i = 0; i < arr.getSize(); i++) {
arr[i] = i * 2;
cout << arr[i] << " ";
}
cout << endl;
} // 스코프 종료 시 자동으로 소멸자 호출
cout << "메인 함수 종료" << endl;
return 0;
}
🎯 실습: 간단한 동적 배열
#include <iostream>
using namespace std;
// 동적 배열 생성과 사용
void demonstrateDynamicArray() {
int size;
cout << "배열 크기를 입력하세요: ";
cin >> size;
// 동적 메모리 할당
int* dynamicArray = new int[size];
// 배열 초기화
cout << "배열에 값을 입력하세요:" << endl;
for(int i = 0; i < size; i++) {
cout << "[" << i << "]: ";
cin >> dynamicArray[i];
}
// 배열 출력
cout << "\n입력된 배열: ";
for(int i = 0; i < size; i++) {
cout << dynamicArray[i] << " ";
}
cout << endl;
// 메모리 해제 (중요!)
delete[] dynamicArray;
cout << "메모리가 안전하게 해제되었습니다." << endl;
}
int main() {
demonstrateDynamicArray();
return 0;
}
🎯 실습: 메모리 누수 방지하기
#include <iostream>
using namespace std;
// 안전한 메모리 관리 함수
int* createAndInitArray(int size, int initValue) {
cout << "메모리 할당: " << size << "개 정수를 위한 공간" << endl;
int* arr = new int[size];
// 배열 초기화
for(int i = 0; i < size; i++) {
arr[i] = initValue + i;
}
return arr;
}
// 배열 처리 함수
void processArray(int* arr, int size) {
cout << "배열 처리 중..." << endl;
for(int i = 0; i < size; i++) {
arr[i] *= 2; // 각 값을 2배로
}
}
// 배열 출력 함수
void printArray(int* arr, int size) {
cout << "배열: ";
for(int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
int main() {
cout << "=== 안전한 동적 메모리 관리 ===" << endl;
const int SIZE = 5;
// 1. 메모리 할당
int* myArray = createAndInitArray(SIZE, 10);
// 2. 초기 상태 출력
cout << "초기 상태: ";
printArray(myArray, SIZE);
// 3. 배열 처리
processArray(myArray, SIZE);
// 4. 처리 후 상태 출력
cout << "처리 후: ";
printArray(myArray, SIZE);
// 5. 메모리 해제 (중요!)
delete[] myArray;
cout << "\n메모리가 안전하게 해제되었습니다." << endl;
return 0;
}
🎮 실습: 성적 관리 시스템
#include <iostream>
#include <string>
using namespace std;
// 간단한 학생 구조체
struct Student {
string name;
double* scores; // 성적 배열을 가리키는 포인터
int scoreCount;
};
// 학생 생성 함수
Student* createStudent(string name, int maxScores) {
Student* student = new Student;
student->name = name;
student->scores = new double[maxScores]; // 성적 배열 동적 할당
student->scoreCount = 0;
cout << "학생 '" << name << "' 생성 (최대 " << maxScores << "과목)" << endl;
return student;
}
// 성적 추가 함수
void addScore(Student* student, double score) {
// scoreCount는 현재 성적 개수를 의미
student->scores[student->scoreCount] = score;
student->scoreCount++;
cout << "성적 " << score << "점이 추가되었습니다." << endl;
}
// 평균 계산 함수
double calculateAverage(Student* student) {
if(student->scoreCount == 0) return 0.0;
double total = 0.0;
for(int i = 0; i < student->scoreCount; i++) {
total += student->scores[i];
}
return total / student->scoreCount;
}
// 학생 정보 출력
void printStudentInfo(Student* student) {
cout << "\n=== " << student->name << "의 성적표 ===" << endl;
cout << "성적: ";
for(int i = 0; i < student->scoreCount; i++) {
cout << student->scores[i];
if(i < student->scoreCount - 1) cout << ", ";
}
cout << endl;
cout << "평균: " << calculateAverage(student) << "점" << endl;
}
// 메모리 해제 함수
void deleteStudent(Student* student) {
delete[] student->scores; // 성적 배열 해제
delete student; // 학생 객체 해제
cout << "학생 데이터가 안전하게 삭제되었습니다." << endl;
}
int main() {
cout << "=== 동적 메모리 성적 관리 ===" << endl;
// 학생 생성
Student* student = createStudent("김철수", 5);
// 성적 추가
addScore(student, 85.5);
addScore(student, 92.0);
addScore(student, 78.5);
addScore(student, 88.0);
// 정보 출력
printStudentInfo(student);
// 메모리 해제
deleteStudent(student);
return 0;
}
💡 모범 사례와 주의사항
✅ 좋은 습관
-
항상 짝을 맞춰라
new
↔delete
new[]
↔delete[]
-
포인터를 nullptr로 초기화
int* ptr = nullptr; if(condition) { ptr = new int(10); } // ... 사용 if(ptr != nullptr) { delete ptr; ptr = nullptr; // 중복 해제 방지 }
-
RAII 패턴 활용
- 생성자에서 자원 할당
- 소멸자에서 자원 해제
❌ 피해야 할 실수
-
메모리 누수
// 잘못된 예 int* ptr = new int(10); return; // delete 없음!
-
이중 해제
int* ptr = new int(10); delete ptr; delete ptr; // 오류!
-
해제된 메모리 접근
int* ptr = new int(10); delete ptr; *ptr = 20; // 위험한 접근!
🔍 디버깅 팁
#include <iostream>
using namespace std;
// 메모리 할당 추적을 위한 간단한 클래스
class MemoryTracker {
private:
static int allocations;
static int deallocations;
public:
static void* allocate(size_t size) {
allocations++;
cout << "[할당] " << size << " 바이트, 총 할당: " << allocations << endl;
return malloc(size);
}
static void deallocate(void* ptr) {
deallocations++;
cout << "[해제] 총 해제: " << deallocations << endl;
free(ptr);
}
static void report() {
cout << "\n=== 메모리 사용 보고서 ===" << endl;
cout << "할당: " << allocations << endl;
cout << "해제: " << deallocations << endl;
cout << "누수: " << (allocations - deallocations) << endl;
}
};
int MemoryTracker::allocations = 0;
int MemoryTracker::deallocations = 0;
Last updated on