Item 16: new 및 delete를 사용할 때는 형태를 반드시 맞추자

std::string *stringArray = new std::string[100];

...

delete stringArray;

이 코드는 문제가 없어보이지만 매우 위험한 코드다. 아마 c언어나 c++ 초보자들이 가장 많이 하는 실수일 것이다.. 해당 코드는 100개의 string 객체들 가운데 99개는 정상적인 소멸 과정을 거치지 못할 가능성이 크다.

new 연산자를 사용해 표현식을 꾸미게 되면(즉, new로 어떤 객체를 동적 할당하면), 이로 인해 두 가지 내부의 동작이 진행된다. 일단 메모리가 할당된다. (이때 operator new가 호출된다.) 그 다음 할당된 메모리에 대해 한 개 이상의 생성자가 호출된다.

delete 표현식을 쓸 경우에는(즉, delete 연산자를 사용할 때는) 또 다른 두 가지의 내부 동작이 진행되는데, 우선 기존에 할당된 메모리에 대해 한 개 이상의 소멸자가 호출되고, 그 후에 그 메모리가 해제된다.

여기서 delete 연산자가 적용되는 객체의 개수는 소멸자가 호출되는 횟수가 된다. 즉, 포인터는 객체 하나만을 가리키고 있기 때문에 소멸자는 단 한 번만 호출된다. 따라서 소멸자가 호출되는 횟수는 1이 된다.

단일 객체의 메모리 배치구조는 객체 배열에 대한 메모리 배치구조와 다르기 때문에 특히, 배열을 위해 만들어지는 힙 메모리에는 대개 배열 원소의 개수가 박혀 들어간다는 점이 가장 결정적이다. 따라서 delete 연산자는 소멸자가 몇 번 호출될지 쉽게 알 수 있다.

반면, 단일 객체용 힙 메모리는 이런 정보가 없다. 따라서 사용자가 직접 배열 크기 정보가 있다는 것을 알려줘야 한다. 이를 []통해 알려줄 수 있다. 그렇지 않으면 그냥 단일 객체라고 간주하기 때문이다.

std::string *stringPtr1 = new std::string;
std::string *stringPtr2 = new std::string[100];

...

delete stringPtr1;
delete[] stringPtr2;

stringPtr1에 delete[]를 사용하는 경우에는 컴파일러는 앞쪽의 메모리 몇바이트를 읽고 이것을 배열의 크기라고 해석하기에 자신이 밝고 있는 메모리가 배열에 속해 있지도 않다는 사실은 물론, 그 메모리에는 자신이 소멸시키려는 타입의 객체가 들어 있지 않게 된다.

정리

정리하자면 new 표현식에 []를 사용했다면 여기에 대응되는 delete 표현식에도 []를 써야 한다는 아주 간단한 규칙이다.

  • new 표현식에 []를 사용했다면 여기에 대응되는 delete 표현식에도 []를 써야 한다. 마찬가지로 new 표현식에 []를 사용하지 않았다면 delete 표현식에도 []를 쓰지 않아야 한다.

사실 요즘에는 new, delete 자체를 사용하지 말고, 스마터 포인터를 사용하거나 컨테이너를 사용하는 것이 좋다. 이런 방법들은 메모리 누수를 줄이고 실수를 줄이는 데 도움이 된다.

태그: ,

카테고리:

업데이트:

댓글남기기