Post List

2015년 1월 4일 일요일

위치지정 new를 작성한다면 위치지정 delete도 같이 준비하자.

 위치지정(placement) new 란 operator new의 기본형과 달리 매개변수를 추가로 받는 형태를 말한다. 개념적으로는 그냥 추가 매개변수를 받는 new 라고 이해하면 된다. 추가매개변수 new의 원조가 위치지정 매개변수를 추가로 받는 것이어서 그냥 그렇게 불린다.

 Widget *pw = new Widget;

 위의 문장은 두 개의 함수가 호출된다. 우선 메모리 할당을 위한 operator new가 호출되고, 그 뒤에 Widget이 기본 생성자가 호출된다. 첫 번째 함수 호출이 무사히 지나갔는데 두 번째 함수 호출이 진행되다가 예외가 발생했을 경우, 첫 단계에서 미이 끝난 메모리 할당을 어떻게 해서든 취소해야 한다. 이 때 매개변수의 개수 및 타입이 똑같은 버전의 operator delete가 호출된다.

 new, delete의 기본형을 사용하고 있었다면 그다지 대수로운 사안이 아니지만 위치지정 new를 사용한 상황에서 같은 형식의 delete가 없다면 new로 저질러버린 메모리 할당을 어떻게 되돌려야 할지 갈팡질팡할 수밖에 없다. 결국은 아무것도 하지 않고 메모리 누수가 발생하게 된다.

 위치지정 delete가 호출되는 경우는 위치지정 new의 호출에 묻어서 함께 호출되는 생성자에서 예외가 발생할 때뿐이다. 정상적으로 delete를 실행 할 경우에는 절대로 위치지정 delete를 호출하는 쪽으로 가지 않는다.

 단, 실수로 빼먹지 말아야 하는 부분이 있다. 이름만 같아도 바깥쪽 유효범위의 모든 함수, 변수, 타입 등을 가려버린다는 사실을 다들 알 것이다.

void* operator new(std::size_t) throw(std::bad_alloc); // 기본형
void* operator new(std::size_t size, void*) throw(); // 위치지정
void* operator new(std::size_t, const std::nothrow_t&) throw(); // 예외불가

 C++가 전역 유효 범위에서 제공하는 operator new의 형태는 다음의 세 가지가 표준이라는 점을 기억해두고, 어떤 형태이든 간에 이 함수들을 가리지 말아야 한다. 이것을 쉽게 해결 할 수 있는 방법이 하나 있다. 기본 클래스에 new, delete의 기본 형태를 전부 넣어두고, 파생 클래스에서는 상속과 using 선언을 2단 콤보로 사용해서 표준 형태를 전부 끌어와 외부에서 사용할 수 있게 만든 후, 원하는 사용자 정의 형태를 선언해 주면 된다.

class StandardNewDeleteForms {
public:
  // 기본형
  static void* operator new(std::size_t size) throw(std::bad_alloc)
  { return ::operator new(size); }
  static void operator delete(void *pMemory) throw()
  { ::operator delete(pMemory); }

  // 위치지정
  static void* operator new(std::size_t size, void* ptr) throw()
  { return ::operator(size, ptr); }
  static void operator delete(void* pMemory, void *ptr) throw()
  { ::operator delete(pMemory, ptr); }

  // 예외불가
  static void* operator new(std::size_t size, const std::nothrow_t& nt) throw();
  { return ::operator new(size, nt); }
  static void operator delete(void* pMemory, vonst std::nothrow_t& nt) throw()
  { ::operator delete(pMemory, nt); }
};

class Widget : public StandardNewDeleteForms {
public:
  using StandardNewDeleteForms::operator new;
  using StandardNewDeleteForms::operator delete;

  static void* operator new(std::sie_t size, std::ostream& logStream)
    throw(std::bad_alloc);
  static void operator delete(void* pMemory, std::ostream& logStream) throw();
  ...
};

 * operator new 함수의 위치지정(placement) 버전을 만들 때는, 이 함수와 짝을 이루는 위치지정 버전의 operator delete 함수도 꼭 만들어 주세요. 이 일을 빼먹었다가는, 찾아내기도 힘들며 또 생겼다가 안 생겼다 하는 메모리 누출 현상을 경험하게 됩니다.

 * new 및 delete의 위치지정 버전을 선언할 때는, 의도한 바도 아닌데 이들의 표준 버전이 가려지는 일이 생기지 않도록 주의해 주세요.



댓글 없음:

댓글 쓰기