C++에서 실행 시간에 필요한 메모리를 할당하고 해제하는 동적 메모리를 다루는 것은 매우 중요한 부분입니다. 동적메모리를 사용하기 위해서는 new와 delete 연산자를 사용합니다. 그러나 이러한 연산자를 잘못 사용하면 메모리 누수(memory leak)나 더블 프리(double free)와 같은 오류가 발생할 수 있습니다. 이를 해결하기 위해 스마트 포인터를 사용하면 동적 메모리 관리를 보다 안전하고 편리하게 할 수 있습니다. 스마트 포인터는 객체의 생명주기를 추적하고, 메모리를 자동으로 해제해 주는 기능을 제공합니다. 이번 글에서는 C++에서 동적 메모리를 다루는 방법과 함께, 이러한 기술들을 사용하여 안전하고 효율적인 메모리 관리를 수행하는 방법에 대해 알아보겠습니다.
동적 메모리 할당과 해제
동적으로 메모리 할당하고 해제하기
동적으로 메모리를 할당하고 해제하기 위해서는 다음과 같이 new / delete 키워드를 사용합니다.
int* ptr = new int; //메모리 할당
*ptr = 10; //값 할당
//...
delete ptr; //메모리 해제
위 코드는 int형 변수를 동적으로 할당하고, 값을 10으로 초기화합니다. new 연산자는 힙(heap)에 메모리를 할당하고, 포인터 변수에 할당된 메모리 주소를 반환합니다. 해제 시에는 delete 연산자를 사용하여 메모리를 해제합니다.
동적으로 메모리 배열 할당하고 해제하기
메모리 배열을 할당하고 해제를하기 위해서는 다음과 같이 new / delete 키워드와 함께 배열 [] 연산자를 사용해야 합니다.
int size = 5;
int* ptr = new int[size]; //메모리 배열 할당
for (int i = 0; i < size; i++) {
ptr[i] = i; //각 배열 인덱스에 값 할당
}
//...
delete[] ptr; //메모리 배열 해제
위 코드는 배열 연산자 []을 사용하여 int형 배열을 동적으로 할당하고, 배열의 크기를 5로 설정합니다. for문을 사용하여 배열의 인덱스에 값을 할당합니다. 배열 해제 시에는 delete와 [] 연산자를 사용하여 해제해야 합니다.
동적으로 2차원 배열 할당하고 해제하기
다음은 이중 포인터와 new, [] 연산자를 사용하여 2차원 메모리 배열을 할당하고 해제하는 방법을 보여줍니다.
int rows = 3, cols = 4;
int** matrix = new int* [rows]; // 전체 메모리 할당
for (int i = 0; i < rows; i++) {
matrix[i] = new int[cols]; // 행단위 메모리 할당
}
// matrix에 데이터를 넣는 작업 수행
// 행 단위로 메모리 해제
for (int i = 0; i < rows; i++) {
delete[] matrix[i];
}
// 전체 메모리 해제
delete[] matrix;
위 코드는 3X4 크기의 2차원 int형 배열을 동적으로 할당하고 있습니다. 먼저 1차원 int형 배열의 포인터를 담을 수 있는 rows 크기의 포인터 배열을 할당하고, 그다음 for문을 사용하여 각 포인터에 cols 크기의 int형 배열을 할당합니다. 메모리 해제 시에는 할당한 순서의 역순으로 해제를 해야 합니다. 먼저 각 행의 메모리를 해제한 다음, 전체메모리를 해제해야 합니다.
메모리 누수와 더블프리
메모리 누수(Memory Leak)
메모리 누수는 메모리를 할당만 하고 해제하지 않았을 때 발생합니다. 다음 예제처럼 new 연산자를 이용하여 메모리를 할당한 후 해제하지 않으면 시스템 리소스가 낭비되고 장기적으로는 프로그램이 예기치 않게 중단될 수 있습니다.
#include <iostream>
int main()
{
int* ptr = new int(10); // 메모리 동적 할당
return 0; // 프로그램 종료 전에 메모리를 해제하지 않음
}
더블프리(Double Free)
더블프리는 다음 예제처럼 이미 해제한 메모리를 한번 더 해제할 때 발생합니다.
#include <iostream>
int main()
{
int* num = new int[10];
delete[] num;
delete[] num; // 더블 프리 오류 발생
return 0;
}
더블 프리 오류는 메모리 누수보다 더 심각한 문제로, 이는 프로그램의 안전성을 해치고 예기치 않은 결과를 초래할 수 있습니다.
예외 처리
new / delete를 정확하게 사용한다고 해도 메모리 문제가 해결되는 것은 아닙니다. new로 메모리를 생성하고 중간에 알 수 없는 이유로 오류가 나서 종료되어 버리면 delete를 하기 전이므로 메모리 누수 같은 문제가 발생할 수 있습니다. 또한 할당 시 시스템 자원 부족으로 실패할 수도 있으므로 이럴 때에는 예외처리 구문을 사용하여 이런 문제를 예방하는 것이 좋습니다.
#include <iostream>
#include <exception>
int main()
{
try {
int* arr = new int[1000000000];
// do something..
delete[] arr;
}
catch (const std::bad_alloc& e) {
std::cerr << "예외 발생!!: " << e.what() << '\n';
return 1;
}
return 0;
}
위 예제는 new 연산자를 사용하여 매우 큰 int 배열을 동적으로 할당하고, delete 연산자를 사용하여 메모리를 해제합니다. 그러나 이 작업이 실패할 수도 있습니다. 만약 시스템에서 이 크기의 메모리를 할당할 수 없는 경우 new연산자는 예외를 던집니다. 이럴 경우를 대비하여 예외처리 구문을 작성하여 프로그램이 비정상적으로 종료되는 것을 방지할 수 있습니다.
스마트 포인터
C++에서는 메모리 문제를 방지하기 위해 스마트 포인터(smart pointer)를 사용할 수 있습니다. 스마트 포인터는 객체의 수명 주기를 추적하고 자동으로 메모리를 해제해 주는 기능을 제공합니다.
스마트 포인터에는 일반적으로 C++11에서 도입된 std::unique_ptr, std::shared_ptr, std::weak_ptr 등이 있습니다. 대표적으로 대부분 많이 사용하는 std::unique_ptr, std::shared_ptr에 대해서 살펴보겠습니다.
std::unique_ptr
std::unique_ptr는 메모리를 할당하고 해당 객체에 대한 고유한 포인터를 유지합니다. 이 포인터는 스마트 포인터가 소멸되면 자동으로 메모리를 해제됩니다.
다음은 std::unique_ptr를 사용하여 동적으로 할당한 객체를 자동으로 해제하는 예제입니다.
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int> myInt = std::make_unique<int>(42);
std::cout << "Value: " << *myInt << std::endl;
return 0;
}
위 예제에서는 make_unique_ptr을 사용하여 myInt라는 스마트 포인터 변수를 만듭니다. 프로그램이 종료되면 스마트 포인터가 자동가 자동으로 메모리를 해제되기 때문에 delete 같은 별도의 처리가 없습니다.
std::shared_ptr
std::shared_ptr는 여러 개의 스마트 포인터에서 같은 객체를 참조할 수 있도록 지원하는 스마트 포인터입니다. 이 스마트 포인터는 참조 횟수를 추적하고, 마지막 참조가 소멸될 때 자동으로 메모리를 해제합니다.
다음은 std::shared_ptr을 사용하여 객체를 참조하는 예제입니다.
#include <iostream>
#include <memory>
int main()
{
std::shared_ptr<int> myInt = std::make_shared<int>(42);
std::shared_ptr<int> myIntCopy = myInt;
std::cout << "Value: " << *myIntCopy << std::endl;
return 0;
}
위 예제에서는 std::shared_ptr을 사용하여 int형 변수를 동적할당하고 해당 변수를 참조합니다. 처음 생성될 때 참조 횟수가 1이고 myIntCopy에 할당하면서 참조 횟수가 2됩니다. 프로그램이 종료시 참조횟수가 0이 되면서 메모리를 해제합니다.
'Program Language > c++' 카테고리의 다른 글
C++ 기초(코딩 스타일) (14) | 2023.03.06 |
---|---|
C++ 기초(std::string_view 클래스) (10) | 2023.03.03 |
C++ 기초(std::string 클래스) (7) | 2023.03.02 |
C++ 기초(Raw String Literal) (13) | 2023.02.20 |
C++ 기초(직원 관리 시스템 - 사용자 인터페이스) (10) | 2023.02.13 |
댓글