Skip to content

Files

Latest commit

e6c5a2b · Jun 14, 2021

History

History
123 lines (105 loc) · 5.45 KB

Item49.md

File metadata and controls

123 lines (105 loc) · 5.45 KB

항목 49. new 처리자의 동작 원리를 제대로 이해하자

operator new

  • 객체 한 개를 할당할때 사용하는 연산자이다.
  • 배열을 담을 메모리의 경우 할당시 operator new[]를 써야 한다.

new 처리자

  • 사용자가 보낸 메모리 할당 요청을 operator new 함수가 맞추어 주지 못할 경우에 operator new 함수는 예외를 던진다.
  • 예외를 던지기 전에, 사용자 쪽에서 지정할 수 있는 에러 처리 함수를 우선적으로 호출할 수 있다.
  • 이 에러 처리 함수를 new 처리자 라고 한다.

set_new_handler

  • 메모리 고갈 상황을 처리할 함수를 사용자 쪽에서 지정할 수 있도록 표준 라이브러리에는 set_new_handler라는 함수가 있다.
namespace std{
    typedef void (*new_handler)();
    new_handler set_new_handler(new_handler p) throw();
}
  • new_handler는 받는 것도 없고 반환하는 것도 없는 함수 포인터에 대해 typedef를 걸어 놓은 타입 동의어이다.
  • set_new_handlernew_handler를 받고 new_handler를 반환하는 함수이다.
    • set_new_handler의 매개변수는 요구된 메모리를 operator new가 할당하지 못했을 때 operator new가 호출할 함수의 포인터이다.
    • ner_Handler의 반환값은 set_new_handler가 호출되기 바로 전까지 new 처리자로 쓰이고 있던 함수의 포인터이다.
void outOfMem()
{
   std::cerr << "Unable to satisfy request for memory";
   std::abort();
}

int main()
{
   std::set_new_handler(outOfMem);
   int *bigDataArray = new int[100000000L];
}
  • operator new가 1억 개의 정수 할당에 실패하면 outOfMem 함수가 호출된다.
  • 이 함수는 에러 메시지를 출력하면서 프로그램을 강제로 끝낸다.
  • 사용자가 부탁한 만큼의 메모리를 할당해 주지 못하면 operator new는 출분한 메모리를 찾아낼 때까지 new 처리자를 되풀이해서 호출한다.

new 처리자에서 해야할일

  • 프로그램 동작에 좋은 영향을 미치는 쪽으로 new 처리자가 설계되어 있다면 아래 동작 중 하나를 꼭 해야 한다.

사용할 수 있는 메모리를 더 많이 확보한다.

  • operator new가 시도하는 이후의 메모리 확보가 성공할 수 있도록 하는 전략
  • 프로그램이 시작할 때 메모리 블록을 크게 하나 할당해 놓았다가 new 처리자가 가장 처음 호출될 때 그 메모리를 쓸 수 있도록 허용하는 방법이 있다.

다른 new 처리자를 설치한다.

  • 현재의 new 처리자가 더 이상 가용 메모리를 확보할 수 없다 해도, 자기 몫까지 해 줄 다른 new 처리자의 존재를 알고 있을 가능성이 있다.
  • 이런 상황에서 현재의 new 처리자는 제자리에서 다른 new 처리자를 설치한다.

new 처리자의 설치를 제거한다.

  • set_new_handler에 널 포인터를 넘긴다.
  • new 처리자가 설치된 것이 없으면, operator new는 메모리 할당이 실패했을 때 예외를 던진다.

예외를 던진다.

  • bac_alloc 혹은 bad_alloc에서 파생된 타입의 예외를 던진다.
  • operator new에는 이쪽 종류의 에러를 받아서 처리하는 부분이 없기 때문에 이 예외는 메모리 할당을 요청한 원래의 위치로 전파된다.

복귀하지 않는다.

  • abort 혹은 exit를 호출한다.

객체의 클래스 타입에 따라 메모리 할당 실패에 대한 처리를 다르게 하는 방법

  • 아래와 같이 할당된 객체의 클래스 타입에 따라 메모리 할당 실패에 대한 처리를 다르게 가져가고 싶은 경우가 존재한다.
class X{
public:
  static void outOfMemory();
  ...
};

class Y{
public:
  static void outOfMemory();
  ...
};

X* p1 = new X; // 메모리 할당이 실패했을 경우 X::outOfMemory를 호출한다.
Y* p2 = new Y; // 메모리 할당이 실패했을 경우 Y::outOfMemory를 호출한다.
  • 클래스에서 자체 버전의 set_new_handleroperator new를 제공하도록 만들어 준다.

set_new_handler

  • 사용자로부터 클래스에 쓰기 위한 new 처리자를 받는다.

operator new

  • 클래스 객체를 담을 메모리가 할당되려고 할 때 전역 new 처리자 대신 클래스 버전의 new 처리자가 호출되도록 만드는 역할
class NewHandlerHolder{
public:
  explicit NewHandlerHolder(std::new_handler nh) : handler(nh) {} // 현재의 new 처리자 획득
  ~NewHandlerHolder()
  {
    std::set_new_handler(handler);                                // 해제
  }

private:
  std::new_handler handler;
  NewHandlerHolder(const NewHandlerHolder&);
  NewHandlerHolder& operator=(const NewHandlerHolder&);
}

class Widget
{
public:
  static std::new_handler set_new_handler(std::new_handler p) throw();
  static void * operator new(std::size_t size) throw(std::bad_alloc);
private:
  static std::new_handler currentHandler;
};

std::new_handler Widget::set_new_handler(std::new_handler p) throw()
{
  std::new_handler oldHandler = currentHandler;
  currentHandler = p;
  return oldHandler;
}

void * Widget::operator new(std::size_t size) throw(std::bad_alloc)
{
  NewHandlerHolder h(std::set_new_handler(currentHandler)); // Widget의 new 처리자 설치
  return ::operator new(size);                              // 메모리를 할당하거나 할당이 실패하면 예외를 던짐
}