1
2
3
4
5 |
std::string* stringArray = new std::string[100];
...
delete stringArray; |
cs |
위 코드를 보면 stringArray가 가리키는 100개의 string 객체들 가운데 99개는 정상적인 소멸 과정을 거치지 못할 것이다.
객체 배열 해제 시에는 []delete를 해야만 하는 이유를 알아보자.
new 연사자를 사용하면 다음과 같은 2가지 내부 동작을 진행한다.
1. 메모리 할당. (operator new라는 이름의 함수가 쓰임.)
2. 할당된 메모리에 대해 한 개 이상의 생성자가 호출.
이와 마찬가지로 delete 연산자를 사용하면 2가지 내부 동작을 진행한다.
1. 할당된 메모리에 대해 한 개 이상의 소멸자가 호출.
2. 메모리 해제. (operator delete라는 이름의 함수가 쓰임.)
여기서 단일 객체를 new / delete 하는 경우와 객체 배열을 new/delete 하는 경우의 메모리 배치구조는 서로 다르다.
단일 객체의 힙 메모리 배치 구조.
Object |
객체 배열의 힙 메모리 배치 구조.
6 |
Object |
Object |
Object |
Object |
Object |
Object |
상기와 같이 대다수의 컴파일러의 경우에는 객체 배열이 힙 메모리에 할당되면 맨 앞에 배열의 크기 정보가 함께 배치된다.
이 때문에, delete 연산자는 소멸자가 몇 번 호출될지를 쉽게 알 수 있게된다.
즉, []delete는 앞쪽의 배열 크기를 읽고, 배열 크기에 해당하는 횟수만큼 소멸자를 호출하게 된다.
만약 delete를 사용하게되면 그냥 단일 객체라 간주하고 소멸자 호출 횟수가 부족해져 메모리 누수가 발생할 것이다.
1
2
3 |
std::string stringObjec = new std::string; //단일 객체.
[]delete stringObjec; //단일 객체를 []delete로 해제. |
cs |
반대로 위와 같이 단일 객체를 해제하려고 할 때, []delete 연산자를 사용한다면... []delete 연산자는 stringObject 객체 메모리 앞쪽의
몇 바이트를 읽어 배열 크기라고 해석할 것이다. 그리고 배열 크기만큼 소멸자를 호출할텐데 결국 엉뚱한 메모리를 해제하려 할테고
알 수 없는 동작이 진행될 것이다.
그러므로 new 연산자에 []를 썼으면, delete 연산자에도 []를 써야 한다는 간단한 규칙만 생각해두면 된다.
1
2
3
4
5
6
7 |
typedef std::string stringArray[4];
std::string *pa1 = new stringArray; //new stringArray이 new string[4]라는 점을 잊으면 안된다.
...
delete[] pa1; |
cs |
또한 typedef로 정의된 어떤 타입의 배열을 생성하려고 new를 썼다면 delete 또한 []를 잊지 말아야 한다.
위의 예제와 같이 typedef를 사용한 경우도 결국 배열이다.
그러나 저렇게 배열 타입이 눈에 명확하게 보이지 않기 때문에, 배열 타입을 typedef 타입으로 만들지 않는 것이 좋다.
1줄 요약
- new 표현식에 []를 썼으면, 대응되는 delete 표현식에도 []를 쓰자.
마찬가지로 new 표현식에 []를 안 썼으면, 대응되는 delete 표현식에도 []를 쓰지 말자.
'Program Language > C++' 카테고리의 다른 글
항목18. 인터페이스 설계는 제대로 쓰기엔 쉽게, 엉터리로 쓰기엔 어렵게 하자. (0) | 2017.08.09 |
---|---|
항목17. new로 생성한 객체를 스마트 포인터에 저장하는 코드는 별도의 한 문장으로 만들자. (0) | 2017.08.09 |
항목15. 자원 관리 클래스에서 관리되는 자원은 외부에서 접근할 수 있도록 하자. (0) | 2017.08.03 |
항목14. 자원 관리 클래스의 복사 동작에 대해 진지하게 고찰하자. (0) | 2017.08.02 |
항목13. 자원 관리에는 객체가 그만! (0) | 2017.07.31 |