본문 바로가기

Studying/Computer Programs

C++ 동적 메모리 할당 (new, delete 키워드)

여기서는 newdelete 키워드를 통해 C++ 프로그램에서 메모리 공간을 동적으로 할당받고 해제하는 방법에 대해서 짚어보겠습니다. 보통 변수나 객체를 소스 코드 내에서 선언하게 되면, 선언한 시점부터 함수나 코드블록 등이 끝날 때 까지 메모리 공간을 점유하게 됩니다. 메모리를 절약해야 하는 상황이라면, 동적 메모리 할당을 통해 한 개 또는 여러개의 변수나 객채를 위한 메모리 공간을 필요할 때만 할당받아 사용하는 것이 좋은 방법입니다.

 

반응형

 

1개의 변수나 객체를 생성하기 위해서는, 포인터 변수를 선언하고 new 키워드 뒤에 자료형이나 클래스의 이름을 붙여주면 되겠습니다. 할당받았던 메모리 공간을 해제하기 위해서는 delete 키워드를 사용하면 되는데요. delete 키워드 뒤에 할당받았던 공간을 가리키는 포인터 변수를 붙여주면 됩니다. 할당받았던 공간이 해제되는 과정에서 변수나 객체 역시 사라지게 되죠.

 

포인터는 메모리 상에서의 주소를 저장하기 위한 변수로서, 포인터가 생소하게 느껴지시는 분들은 시작하기에 앞서서 다음 포스팅을 읽어보시면 큰 도움이 되리라 생각합니다.

 

 

C언어 프로그래밍 기초 : 포인터

여기서는 C언어와 C++가 다른 프로그래밍 언어와 차별화되는 요소 중 하나인 포인터 (pointer)에 대해서 알아봅시다. 포인터의 기본 개념과 함께 일반적인 데이터를 저장하는 변수들과의 상관관계

swstar.tistory.com

 

다음 예시는 1개의 정수형 변수를 생성하고, 그 값을 10으로 정의한 뒤에 메모리 공간을 해제하는 내용을 담고 있는 코드입니다.

 

/* 할당받은 메모리 공간의
 * 변수를 가리키는 포인터 */
int *ptr_i;

/* 1개의 정수형 변수를 위한
 * 메모리 공간 할당 */
ptr_i = new int;

// 변수의 값을 정의
(*ptr_i) = 10;

// ... 변수의 값 사용 ...

/* 동적으로 할당받았던
 * 메모리 공간을 해제 */
delete ptr_i;

 

new int를 통해서 1개의 정수형 변수를 위한 공간을 할당받고, 이와 동시에 정수형 변수 1개가 생성이 됩니다. 이 변수 자체는 이름을 가지고 있지 않지만, 그 주소를 가리키는 포인터 변수 ptr_i를 통해서 접근 및 제어가 가능합니다.

 

실무적으로는 C언어의 malloc 함수처럼, 동일한 자료형을 가진 여러개의 변수 혹은 객체를 위한 공간을 할당받아서 사용하는것이 일반적입니다. 이 때는 할당받고자 하는 변수나 객체의 갯수를 대괄호 ([ ])안에 넣고 자료형이나 클래스 이름 뒤에 붙여주도록 합시다.

 

이렇게 할당받은 공간에 저장된 변수나 객체에 접근하기 위해서는 대괄호 안에 인덱스의 값을 넣고 메모리 주소를 가리키는 포인터 변수 이름 뒤에 붙여 주면 되겠습니다. 여기서 인덱스는 0 부터 [배열의 크기]-1 까지의 값을 가질 수 있습니다. 그리고 더 이상 필요가 없어져서 할당받은 공간을 해제하고자 하는 경우에는 delete 키워드 뒤에 대괄호를 붙여야 합니다.

 

다음 예시는 동적 메모리 할당을 통해 크기가 10인 정수형 배열을 생성하고, 저장된 변수들의 값을 인덱스와 동일하게 정의하는 코드입니다.

 

/* 동적으로 할당받고자 하는
 * 배열의 크기 */
int size_i = 10;
/* 동적으로 할당받은 배열의
 * 주소를 가리키는 포인터 */
int *array_i;

// 동적 메모리 할당
array_i =
    new int [size_i];

// 배열의 값 정의
for (int i = 0; i < size_i; i++) {
    array_i[i] = i;
}

// ... 배열의 값 사용 ...

// 할당받았던 메모리 공간을 해제
delete [] array_i;

 

1차원 배열 뿐만 아니라, 다중 포인터를 통해서 행렬이나 텐서를 위한 공간을 할당받는 것도 가능합니다. 예를 들어서 2중 포인터를 이용해서 행렬을 만들고 싶다면, 먼저 각 행 (row)을 가리키기 위한 포인터들의 배열을 할당받은 다음, 다시 각 행을 가리키는 포인터를 통해서 행렬의 값을 저장하기 위한 메모리 공간을 할당받으면 되겠습니다.

 

다음 예시는 크기가 5인 단위 행렬을 위한 공간을 할당받아, 그 값을 정의하는 코드입니다.

 

// 정사각행렬의 크기
int size_m = 5;
/* 행렬의 값을 저장하기 위한
 * 2중 포인터 */
double **mtx_identity;

/* 첫번째 단계 :
 * 행 (row)을 위한 동적 메모리 할당
 * mtx_identity는 포인터의 베열을 가리킴. */
mtx_identity = new double *[size_m];
// 행 (row) 루프
for (int ir = 0; ir < size_m; ir++) {
    /* 두번째 단게 :
     * 열 (column)을 위한 동적 메모리 할당
     * mtx_identity[ir]은 각 행의 배열을
     * 가리킴. */
    mtx_identity[ir] =
        new double [size_m];

    /* ir 행 - ic 열에서의
     * mtx_identity[ir][ic]의 값 정의 */
    for (int ic = 0; ic < size_m; ic++) {
        if (ir == ic) {
            mtx_identity[ir][ic] = 1.;
        } else {
            mtx_identity[ir][ic] = 0.;
        }
    }
}

// ... 할당받은 변수 사용 ...

// 동적으로 할당받은 메모리 해제
for (int ir = 0; ir < size_m; ir++) {
    /* 먼저 각 행에 대해 할당받은
     * 메모리 공간을 해제 */
    delete [] mtx_identity[ir];
}
/* 그 다음 행들을 가리키는 포인터들의
 * 배열을 해제 */
delete [] mtx_identity;

 

클래스 내부에서도 new, delete 키워드를 이용해서 동적으로 메모리를 관리할 수 있는데요. 예를 들어서 클래스를 초기화하는 함수에서 동적으로 메모리를 할당하고, 객체를 삭제할 때 호출되는 소멸자에서 할당받았던 메모리 공간을 해제하는 방식으로 코드를 구성할 수 있습니다.

 

class MyClass {
  private :

    // 배열의 크기
    int size_i_;
    /* 동적으로 할당받은 배열을
     * 가리키는 포인터 */
    int *array_i_;

    /* 객체가 초기화되어
     * 동적 메모리 할당이 이루어졌는지
     * 알려주는 제어 변수 */
    bool initialized_;

  public :

    // 생성자
    MyClass() {
        size_i_ = 0;
        array_i_ = NULL;

        initialized_ = false;
    }

    // 소멸자
    ~MyClass() {
        free_array();
    }

    /* 객체를 초기화하고
     * 동적으로 메모리를 할당받기
     * 위한 함수 */
    void init(int size_in) {
        free_array();

        size_i_ = size_in;
        if (size_i_ < 1) {
            return;
        }

        array_i_ =
            new int [size_i_];
        for (int i = 0; i < size_i_; i++) {
            array_i_[i] = i;
        }

        initialized_ = true;
    }

    /* 동적으로 할당받았던 메모리를
     * 해제하기 위한 함수 */
    void free_array() {
        if (!initialized_) {
            return;
        }

        delete [] array_i_;

        size_i_ = 0;
        array_i_ = NULL;

        initialized_ = false;
    }
};

 

위의 예시에 등장하는 MyClass라는 클래스의 init이라는 멤버 함수는 동적으로 할당되는 배열의 크기를 인자로 받고 있습니다. 메모리를 할당받고 나면, 변수 initialized_의 값을 true로 정의하는데요. 이 제어변수가 필요한 이유는 소멸자에서 호출되는 free_array 함수에서 메모리 공간을 해제할지의 여부를 결정해야 하기 때문입니다. 메모리 공간을 해제하고 나면 initialized_의 값을 false로 바꿔서, 같은 주소를 가진 메모리 공간이 이중으로 해제되는 것을 방지합니다.

 

new, delete 키워드들을 통해 C++ 프로그램에서 메모리를 동적으로 관리하는 방식은 C언어의 mallocfree 함수와 유사한데요. malloc 함수를 통해 변수들을 저장하기 위한 메모리 공간을 할당받고, free 함수를 통해 해제하기 때문입니다. 중요한 차이점 중의 하나는, new 키워드와는 달리 malloc 함수는 클래스로부터 객체 배열을 할당받는데는 사용할 수 없다는 것입니다. C언어에서 malloc, free 함수들을 가지고 메모리를 동적으로 관리하는 방법에 대해서는 다음 포스팅을 참고하면 좋습니다.

 

 

C언어 동적 메모리 할당 (malloc, free 함수)

일반적으로 C언어에서 변수나 배열을 선언하면, 해당 코드블록이 끝나서 범위를 벗어날때 까지 메모리를 차지하게 됩니다. 하지만 이렇게 되면 메모리 공간의 낭비가 발생할 수 있죠. 필요할 때

swstar.tistory.com

 


 

같이 알고 있으면 좋은 C/C++ 팁들

 

함수를 인자로 사용하기

 

C/C++ 에서 함수를 매개변수로 사용하기

함수 포인터를 이용한 구현 일반적으로 C언어나 C++ 에서 사용하는 함수의 경우, 인자(매개변수) 혹은 파라미터로 변수를 받아갑니다. 이 값들을 가지고 정의된 기능을 수행하게 되죠. 하지만 프

swstar.tistory.com

 

C언어 라이브러리 제작

 

C언어 라이브러리 만들기

자주 쓰는 함수들을 라이브러리 형태로 만들어 두면, 프로그램을 짤 때 편리합니다. 오브젝트 파일로부터 라이브러리를 제작하고, 이를 헤더 파일과 함께 이용하는 방법을 알아볼텐데요. 만약

swstar.tistory.com

 

OpenMP를 이용한 병렬 프로그래밍

 

OpenMP C/C++ 를 이용한 병렬 프로그래밍

개요 여기서는 여러개의 CPU 코어를 동시에 사용하는 병렬 프로그램을 만들기 위한 방법 중 하나인 OpenMP에 대해 알아봅시다. OpenMP는 여러 개의 명령문들이 동시에 실행되는 프로그램을 작성하기

swstar.tistory.com