namespace std { typedef void (*new_handler)(); new_handler set_new_handler(new_handler p) throw(); } void outOfMem() { std::cerr << "Unable to satisfy request for memory\n"; std::abort(); } int main() { std::set_new_handler(outOfMem); int *pBifDataArrat = new int[100000000L]; ... }
new-handler가 어떤 것인지 대충 알아볼수 있게 해주는 예시이다. 하지만 이것도 완벽하진 않다. new-handler애서 문자열을 또 할당해야하는 Code가 있는데 만약 저기서 또 에러가 발생한다면 무한루프에 빠질수도...
new-handler는 다음 동작 중 하나를 꼭 해주어야 한다.
- 사용할 수 있는 메모리를 더 많이 확보 : ex)시작시 메모리 블록을 크게 하나 할당해 놓았다가 여기서 그 메모리를 사용할 수 있게 허용하는 등...
- 다른 new 처리자를 설치 : 현재 new-handler 안에서 set_new_handler를 호출
- new-handler의 설치를 제거 : set_new_handler(NULL); 이러면 예외를 던지게 됨
- 예외를 던짐 : bad_alloc 나 거기서 파생된 예외를 던짐
- 복귀하지 않음 : abort 또는 exit 호출
특정 class만을 위한 new-handler를 두고 싶다면, 직접 구현해야 한다. class 내에 new-handler 타입의 정적 멤버 데이터를 선언해서 거기에 보관해두고 필요할 때 set_new_hander로 선언을 해두며, 처리가 끝난 뒤 원래 new-handler로 다시 돌려주면 된다. new 구현에는 new-handler이 자원관리를 위한 RAII class를 이용하였다.
class NewHandlerHolder { public: explicit NewHandlerHolder(std::new_handler nh) : handler(nh) {} ~NewHandlerHolder() { std::set_new_handler(handler); } private: std::new_handler handler; NewHandlerHolder(const NewHandler&); NewHandlerHolder& operator= (const NewHandlerHolder&); }; class Widget { public: static std::new_handler set_new_handler(std::new_handler p) throw() { std::new_hander oldHandler = currentHandler; currentHandler = p; return oldHandler; } static void* operator new(std::size_t size) throw(std::bad_alloc) { NewHandlerHolder h(std::set_new_handler(currentHandler)); return ::operator new(size); } private: static std::new_handler currentHandler; // 클래스 구현 파일에서 NULL로 초기화 필요 };
잠시만 생각해 보면, 이런 Code는 어떤 클래스를 쓰더라도 똑같이 나올 것 같다. 이런 용도에 손쉽게 쓸 수 있는 손질 방법으로는 믹스인(mixin) 양식의 기본 클래스를 추천하고 싶다. 템플릿으로 구현하여 각 클래스 별로 만들어지긴한데 그 안 어디에도 템플릿타입에 대한 Code는 없다. 이런 형식을 신기하게 반복되는 템플릿 패턴(curiously recurring template pattern: CRTP)라고 부른다.
template<typename T> class NewHandlerSupport { 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; }; template<typename T> std::new_handler NewHandlerSupport<T>::set_new_handler(std::new_handler p) throw() { std::new_handler old = currentHandler; currentHandler = p; return old; } template<typename T> void* NewHandlerSupprot<T>::operator new(std::size_t size) throw(std::bad_alloc) { NewHandlerHolder h(std::new_handler(currentHandler)); return ::operator new(size); } template<typename T> std::new_handler NewHandlerSupport<T>::currentHandler = 0; class Widget : public NewHandlerSupport<Widget> { ... };
* set_new_handler 함수를 쓰면 메모리 할당 요청이 만족되지 못핼을 때 호출되는 함수를 지정할 수 있습니다.
* 예외불가(nothrow) new는 영향력이 제한되어 있습니다. 메모리 할당 자체에만 적용되기 때문입니다. 이후에 호출되는 생성자에서는 얼마든지 예외를 던질 수 있습니다.
댓글 없음:
댓글 쓰기