728x90
반응형
std::string* stringArray = new std::string[100];
...
delete stringArray;

위 코드를 보면 stringArray가 가리키는 100개의 string 객체들 중 99개는 정상적인 소멸 과정을 거치지 못할 것이다.

 

new와 delete 연산자의 동작

new 연산자를 사용해 어떤 객체를 동적 할당하게 되면, 두 가지의 내부 동작이 이루어진다.

1. 메모리가 할당된다. (operator new라는 이름의 함수가 쓰인다. 항목 49, 51 참조)

2. 할당된 메모리에 대해 한 개 이상의 생성자가 호출된다.

 

delete 연산자는 다음과 같은 두 가지 내부 동작이 이루어진다.

1. 기존에 할당된 메모리에 대해 한 개 이상의 소멸자가 호출된다.

2. 그 후에 메모리가 해제된다. (operator delete)

 

그렇다면 delete연산자가 적용되는 객체는 몇 개일까?

바로 소멸자가 호출되는 횟수만큼 delete연산자가 적용된다.

 

단일 객체와 객체 배열에 대한 메모리 배치 구조

new로 힙에 할당된 단일 객체의 메모리 배치 구조는 객체 배열에 대한 메모리 배치 구조와 다르다.

특히 배열을 위해 만들어지는 힙 메모리에는 대게 배열 원소의 개수가 들어간다는 점이 가장 결정적인데, 이 때문에  delete연산자는 소멸자가 몇 번 호출될지 쉽게 알 수 있다.

반면, 단일 객체용 힙 메모리에는 이런 정보가 없다.

 

단일 객체의 힙 메모리 배치 구조

Object

 

객체 배열의 힙 메모리 배치 구조

원소의 개수 Object Object Object Object Object

 

이를 바탕으로 어떤 포인터에 대해 delete를 적용할 때, delete연산자로 하여금 '배열 크기 정보가 있다'는 것을 알려주려면 대괄호 쌍([])을 delete뒤에 붙여주면 된다.

그럼 앞쪽의 배열 크기를 읽고, 배열 크기에 해당하는 횟수만큼 소멸자를 호출하게 된다.

 

만약 단일 객체를 'delete []' 형태로 메모리를 해제하려고 하면 어떤 일이 발생할까?

보통 객체 메모리 앞쪽의 몇 바이트를 읽어 배열 크기라고 해석할 것이며, 배열 크기만큼 소멸자를 호출할 텐데 해제하고 싶은 메모리가 단일 객체 메모리므로 결국 엉뚱한 메모리를 해제하게 될 것이다.

 

즉, new 연산자에 []를 사용했으면 delete연산자에도 []를 써야 한다는 규칙만 알아두면 된다.

 

typedef 주의 사항

아래와 typedef로 정의된 어떤 타입의 메모리를 해제할 경우, 어떤 형태의 delete를 사용해야 하는지는 typedef의 정의에 따라 달라진다.

예를 들어, typedef 타입이 다음과 같이 배열로 정의되어 있다면 해제도 delete []로 해줘야 한다.

typedef string AddressLines[4];

string *pa1 = new AddressLines; // new string[4] 와 같은 뜻
delete[] pa1;

 

요약

  • new 표현식에 []를 썼으면, 대응되는 delete 표현식에도 []를 써야 합니다. 마찬가지로 new 표현식에 []를 안 썼으면, 대응되는 delete 표현식에도 []를 쓰지 말아야 합니다.
728x90
반응형