728x90
반응형

operator new의 기본 요구사항

1. 반환 값이 제대로 되어있어야 한다.

  • 요청된 메모리를 마련할 수 있는 경우 반환 값은 메모리에 대한 포인터이다.
  • 요청된 메모리를 마련할 수 없는 경우 bad_alloc 타입의 예외를 던진다.

2. 가용 메모리가 부족할 경우에는 new 처리자 함수를 호출해야 한다.

3. 크기가 없는 메모리 요청(0바이트)에 대한 대비책을 갖춰두어야 한다.

4. 기본 형태의 new가 가려지지 않도록 한다.

 

비멤버 버전의 operator new 함수 의사 코드

operator new 멤버 함수는 파생 클래스 쪽으로 상속이 되는 함수이다.

// 다른 매개변수를 추가로 가질 수 있다.
void * operator new(std::size_t) throw(std::bac_alloc) 
{
  using namespace std;
  if (size == 0)
  {
    size = 1; // 0 바이트 요청이 들어오면 1 바이트 요구로 간주하고 처리
  }

  while(true)
  {
    size 바이트 할당
    if (할당 성공)
      return (할당된 메모리에 대한 포인터)

    // 할당이 실패했을 경우
    // 현재의 new 처리자 함수가 어느 것으로 설정되어 있는지 찾는다.
    new_handler globalHandler = set_new_handler(0);
    set_new_handler(globalHandler);

    if(globalHandler) (*globalHandler)();
    else throw std::bac_alloc();
  }
}

보면 무한루프(while(true) 문)를 빠져나오는 방법으로는 메모리 할당이 성공하거나 아니면 항목 49에서 이야기한 동작들 중 한 가지를 new 처리자 함수 쪽에서 해주든지 둘 중 하나이다.

new 처리자에서 처리를 안 하면 operator new의 내부 루프는 스스로 끝나지 않는다.

 

operator new클래스 전용 버전

class Base
{
public:
  static void * operator new(std::size_t size) throw(std::bad_alloc);
};

class Derived : public Base {...}; // Derived에서는 operator new가 선언되지 않는다.
derived * p = new Derived; // Base::operator new가 호출된다.

void * Base::operator new (std::size_t size) throw(std::bad_alloc)
{
  if (size != sizeof(base))
  {
    // 틀린 크기가 들어오면 표준 operator new 쪽에서 메모리 할당 요구를 처리한다.
    return ::operator new(size); 
  }

  // 맞는 크기가 들어오면 메모리 할당 요구를 여기서 처리한다.
}

C++ 에는 모든 독립 구조의 객체는 반드시 크기가 0이 넘어야 한다는 금기사항이 있기 때문에, sizeof(Base)와 size를 비교하는 코드에서 0바이트 점검도 함께 진행한다. size가 0이면 메모리 처리 요구가 ::operator new 쪽으로 넘어간다.

배열에 대한 메모리 할당을 클래스 전용 방식으로 하고 싶다면 operator new [] 함수를 구현하면 된다.

 

operator delete 작성 규칙

operator delete를 작성할 때의 관례는 C++는 널 포인터에 대한 delete 적용이 항상 안전하도록 보장한다.

아래의 비멤버 버전 operator delete의 의사 코드를 보자.

void operator delete(void *rawMemory) throw()
{
  // 널 포인터가 delete되려고 할 경우에는 아무것도 하지 않는다.
  if(rawMemory == 0) return; 

  // rawMemory가 가리키는 메모리 해제
}

 

operator delete 클래스 전용 버전

operator delete의 클래스 전용 버전도 단순하다. 삭제될 메모리의 크기를 점검하는 코드를 넣어주기만 하면 된다.

operator new 쪽으로 틀린 크기의 메모리 요청을 ::operator new 쪽으로 넘기도록 구현되었다고 가정하면, 클래스 전용의 operator delete 역시 틀린 크기로 할당된 메모리의 삭제 요청을 ::operator delete 쪽으로 전달하는 식으로 구현하면 된다.

class Base
{
public:
  static void operator delete(void * rawMemory, std::size_t size) throw();
};

void Base::operator delete(void *rawMemory, std::size_t size) throw()
{
  if (rawMemory == 0) return; // 널 포인터 점검

  // 크기가 틀린 경우 표준 operator delete가 메모리 삭제 요청을 맡는다.
  if (size != sizeof(Base)) 
  {
    ::operator delete(rawMemory);
    return;
  }

  // rawMemory가 가리키는 메모리를 해제한다.

  return;
}

가상 소멸자가 없는 기본 클래스로부터 파생된 클래스의 객체를 삭제하려고 할 경우에는 operator delete로 C++가 넘기는 size_t 값이 엉터리일 수 있다. 기본 클래스에서 가상 소멸자를 빼먹으면 operator delete 함수가 똑바로 동작하지 않을 수 있다.

 

요약

  • 관례적으로, operator new 함수는 메모리 할당을 반복해서 시도하는 무한 루프를 가져야 하고, 메모리 할당 요구를 만족시킬 수 없을 때 new 처리자를 호출해야 하며, 0바이트에 대한 대책도 있어야 한다. 클래스 전용 버전은 자신이 할당하기로 예정된 크기보다 더 클(틀린) 메모리 블록에 대한 요구도 처리해야 한다.
  • operator delete 함수는 널 포인터가 들어왔을 때 아무 일도 하지 않아야 한다. 클래스 전용 버전의 경우에는 예정 크기보다 더 큰 블록을 처리해야 한다.
728x90
반응형