내가 C++에 조예가 깊어서 글을 남기는 것이 아니라, Effecitve C++ 을 공부하는 사람들이 이 글을 보고, 도움이 되었으면 하는 생각과, 혹시 내가 틀린것이 있다면 지적해 주시지 않을까 란 생각으로 글을 올리는것임을 미리 밝힙니다.  - 최익필

이번 항목은 new 처리자에 대한 개념과 어디에 주로 사용되는지에 대해서 알려 준다.

new 처리자 곰곰히 들으면 잘 모르니, 우선 말로만 설명 풀이 해보자. 우리는 메모리를 할당하기 위해서 new 연산을 이용한다. 하지만 자세히 들어가보면 operator new 가 호출되고 new 가 호출되는 구조로 이어진다.

그러니까 operator new -> new 를 처리 한다. 이때 operator new 는 우리 스스로가 오버로딩 하여 재정의 할 수 있지만 new는 .. 재정의가 되지 않는다. 뭐.. 이건 그렇다는것이고 ..

이런 new 연산을 통하여 메모리를 할당하게 되는데 메모리 할당을 여러가지 이유로 못하게 되었을 경우, 예외를 던진다. 예전에는 NULL 포인터를 반환했지만 말이다.

이렇게 예외를 던지는데, C++은 이런 예외를 던지기 전에 우선 처리되게 할수 있게 만들어 두었는데, 이것을 처리자 라고 한다. 영어로 new-handler 라고 하는데, 기본적으로 이 처리자가 셋팅되어 있지만, 사용자가 임의로 변경을 할 수 있다.

그 처리자를 변경하는 함수는 std:set_new_handler(인자) 를 통하여 할수 있고 이 인자는 void 형 반환에 void형 매개변수를 같는 함수 이다.


이렇게 말하면 어떻게 하는지 모르겠으니, 소스 코드를 첨부한다.

이렇게 하면 내가 정의한 함수가 new-handler가 된다.


이제 개념을 잡았으니, 어떻게 하면, new-handler를 효율적으로 짤 수 있는지 그 5가지 방법에 대해서 알아보자.
1. 사용할 수 있는 메모리를 더 많이 확보한다.
: 이 이야기는 프로그램 초기 진입시 많은 량의 메모리를 할당받아 놓고, 예외가 발생 되기 직전이라면, new-handler가 호출될테니 미리 잡아 두었던 메모리에 접근 할수 있도록 하자는 것이다.

2. 다른 new-handler 를 설치한다.
: 현재의 new-handler 가 해결 할수 없다면, 이것까지 커버할수 있는 new-handler를 새롭게 셋팅하고, 다시 한번 메모리 할당이 실패가 되었을 때 새롭게 설치한 new-handler를 이용하는것이다. (보통 new-handler가 로그목적으로 사용 할 때, 이용 되는듯 싶다.)

3. new 처리자의 설치를 제거한다.
: std::set_new_handler에 NULL 포인터를 넣어 곧바로 예외를 던지게 하는 것이다.

4. 예외를 던진다.
: bad_alloc or bad_aloc에서 파생된 타입의 예외를 던지게 한다. (이 녀석시 new-handler가 로그 목적으로 사용 될 때, 이용 되는 듯 싶다.)

5. 복귀하지 않는다.
: 바로... 프로그램을 종료한다. abort or exit 호출!


이렇게 new-handler를 셋팅하면, 융통성 있게 대처할 수 있을 것이라고 스콧 마이어스님은 말씀 하신다.


이제 어떻게 new-handler를 사용하고, 주의해야 할 점에 대해서 알아보자.

나는 내가 정의한 클래스의 객체를 동적 메모리 할당 했을때 에러가 발생하면, 그 클래스에 맞는 new-handler를 사용 하고 싶다고 치자. 어떻게 할까?

소스를 보면.. RAII 형식으로 new-handler를 처리 하는데, 이것은 거의 습관적으로 이렇게 짜는 것으로 보면 될듯 하고.. 실제로 에러가 발생되면 CBASE에 셋팅했던 new-handler가 호출 되어 처리가 된다.

=
그런데.. MSVC2005에선 이런 에러가 발생한다.
"warning C4290: 함수가 __declspec(nothrow)가 아님을 나타내려는 경우를 제외하고 C++ 예외 사양은 무시됩니다." .. 모야? 왜 std::bad_alloc() 을 무시 하는거지? 하고 조사를 해보았다.
그러니.. 이런 답변이 있더라.. http://www.gpgstudy.com/forum/viewtopic.php?t=18428
=

다시 본론으로 돌아 오면, 기본적으로 이 구조를 가지고 모든 클래스의 new-handler를 셋팅하면 될꺼 같다. 모든 클래스 마다 이런 .. 코드르 짜도 되지만, 왠지.. 손가락이 불쌍하다. 이럴 때.. 템플릿 메타 프로그래밍을 이용 하면 아주 손쉽게 모든 클래스가 이 코드를 자동으로 사용 할수 있게 만들 수 있다.

이런 특정한 기능만 클래스로 구현하여 public 상속하는 mixin 양식이 있고, 템플릿 메타 프로그래밍과 합친다면, 보다 효과적으로 가능하다.(.. 이 기법이 궁금하신 분은 ikpil@naver.com 으로 코드 보내 드리겠습니다. 지금 코드가 없어서..)

이렇게 set_new_handler를 이용하여, 클래스에 맞는 new-handler 셋팅 방법과 개념을 잡았다. 이런식으로 new-handler를 셋팅하여 new에 대한 예외처리를 했다면, 아예 "예외불가(nothrow)"라는 형태도 있다.

사용 방법운 new (std::nothrow) CBASE; 형식으로 할당 하면 되고, 예외발생시 예외를 던지지 않고, NULL 포인터를 반환하게 해준다.

사실 1993년까지의 C++에선 operator new가 메모리를 할당 할수 없다면 NULL 로 반환 하도록 되어 있었지만, 몇년이 지난 후에 bad_alloc 예외를 던지도록 명세가 바뀌었다. 하지만 이미 많은 컴파일러들이 .. NULL 을 반환하도록 구현을 했던 상태고, 위원회에서도 NULL 포인터 정검 기반의 코드를 버리고 싶지 않았다.

그래서 만든것이 예외 불가 형태이다. (책에서 나온 내용 요약)


이것만은 잊지 말자.
1. set_new_handler 함수를 쓰면 메모리 할당 요청이 실패했을 때, 호출 되는 함수를 지정 할 수 있다.
2. 예외불가(nothrow) new는 영향력이 제한되어 있어서, 생성자에서 예외 발생시 예외를 던질 수 있따.
3. http://www.gpgstudy.com/forum/viewtopic.php?t=18428 꼭 읽어 봐야 한다,



신고
posted by 농사를 짓는 게임 프로그래머 최익필

댓글을 달아 주세요