std::string* stringArray = new std::string[100];
...
delete stringArray;
이 코드는 미정의 동작으로 빠져버린다.
new[]
로 메모리를 할당하고 delete
로 해제했기 때문이다.
설령 동작하더라도 stringArray
가 가리키는 100개의 string 객체들 중 99개의 객체들은 소멸하지 못할 것이다.
new 표현식 사용
1. 메모리 할당
2. 할당된 메모리에 대해 한 개 이상의 생성자 호출
delete 표현식 사용
1. 기존에 할당된 메모리에 대해 한 개 이상의 소멸자 호출
2. 메모리 해제
단일 객체의 메모리 배치 구조와 객체 배열의 메모리 배치 구조는 다르다.
특히, 배열을 위해 만들어지는 힙 메모리에는 대개 배열원소의 개수가 존재한다. 이는 delete
연산자가 적용되는 객체의 개수이며 즉 소멸자가 호출되는 개수이다.
그렇다면 delete
를 적용할 때 delete
연산자에게 배열에 크기 정보가 있다는 것을 알려줄 책임은 사용자에게 있다. 때문에 delete
가 아닌 delete[]
를 사용해야 한다. []
를 붙이지 않으면 객체 배열이 아닌 단일 객체라고 인식하게 된다.
std::string* stringPtr1 = new std::string;
std::string* stringPtr2 = new std::string[100];
delete stringPtr1; // 단일 객체 삭제
delete [] StringPtr2; // 객체 배열 삭제
이렇게 단일 객체인 경우 delete
를, 객체 배열인 경우 delete[]
를 통해 메모리를 해제해야 한다.
delete [] stringPtr1;
delete stringPtr2;
단일 객체 stringPtr1
에 delete[]
를 사용한다면??
stringPtr1
앞쪽의 메모리 몇 바이트를 읽고 이를 배열 크기라고 해석한다.
이후 배열 크기의 개수만큼 소멸자를 호출할 것이고 소멸자를 호출하고 있는 메모리는 배열에 속해 있지도, 그 메모리가 자신이 소멸시키려는 타입의 객체도 아닐 것이다.
객체 배열 stringPtr2
에 delete
를 사용한다면??
소멸자가 호출되어야 하는 횟수보다 더 적게(1번) 호출될 것이다.
new와 delete의 형태를 맞춰야 한다는 규칙은 typedef
을 사용할 때 특히 주의해야 한다.
typedef std::string AddressLines[4];
std::string *pal = new AddressLines; // new 연산자를 사용한 것으로 보이지만 실제로는 new string[4]
delete [] pal; // new AddressLines만 보고 delete 연산자를 사용하면 안된다.
이렇게 메모리를 할당한 코드만 보고서는 delete
를 쓸 지, delete[]
를 쓸 지 알 수 없으므로 배열 타입은 typedef
으로 만들지 않는 것이 좋다.
참고로 해결법은 간단하다. 배열 말고 vector<string>
으로 정의하면 된다.
정리
각 표현식에 대응되는 표현식을 쓰자!
new
=>delete
new[]
=>delete[]