C++ set_new_handler 예제

C++ 에서 메모리 할당에 실패하는 경우에 대비하기 위해 어떻게 해야될지를 알아봅니다.

이런 상황에 대처하는 방법은 크게 보면 3가지가 있습니다.

1. 예외처리로 감싸줍니다.
2. assert 함수를 이용하여, 개발단계에서 메모리 할당 실패에 대비하는 코딩하도록 합니다.
3. handler 함수를 정의하여, 메모리 할당 실패시 실행해야 할 일을 정의해줍니다.

이 포스트에서는 핸들러 함수를 정의하여 사용하는 방법에 대해 알아보도록 하겠습니다. 핸들러 함수를 정의하고 등록해두면, new 가 메모리 확보에 실패시 등록해둔 handler 함수를 호출합니다. 이 함수를 사용하기 위해서는 new 헤더 파일을 포함시켜줘야 하고, 함수 포인터 기법을 사용해야 합니다.

 #include <new>
 . . .
 typedef void (*PTRF)();
 PTRF set_new_handler(PTRF func_name);

3번째 줄의 typedef 구문에서는 PTRF를 인자없고 반환 타입 없는 함수에 대한 포인터 타입으로 정의했습니다. (PTRF 는 보통 Pointer to Function의 약자입니다.) 4번째 줄에서는 set_new_handler 라는 함수를 정의하고 인자는 PRTF, 그리고 반환값도 PTRF 입니다.

#include <iostream>
#include <new>
#include <cstdlib>

using namespace std;

const int bsize = 1024*10000;//10M
int *pInt;
volatile bool bAllocated = true;

void newFailHandler() 
{
	cerr << "No more free store space available" << endl;
	delete [] pInt;           // release saved block
	bAllocated = false;
}

void allocMemory(int size) 
{
	int *p = new int[size];       // allocate memory
	if (bAllocated)
		allocMemory(size);         // recursive call
	else
		cerr << "free store addr = " << p << endl;
}

int main()
{
	set_new_handler(newFailHandler);    // specify handler
	pInt = new int[bsize];       // block to save
	cerr << "free store addr = " << pInt << endl;
	allocMemory(bsize);
	return 0;
}

/* output

free store addr = 00E80040
No more free store space available
free store addr = 00E80040
계속하려면 아무 키나 누르십시오 . . .

*/

위코드는 VS2010 에서 실행한 결과입니다.

main 함수를 보면 첫번째로 newFailHandler() 라는 함수를 이용하여 set_new_handler 함수를 호출하고 있습니다. newFailHandler() 함수는 여기서 핸들러 함수역할을 합니다.

두번째와 세번째 호출은 10M 만큼의 int 배열을 할당한 후, 이를 출력합니다.

네번째 호출인 allocMemory 함수를 호출하는 부분을 눈여겨 봐야 하는데, 그 이유는 allocMemory 내부 구현이 재귀호출로 되어있기 때문입니다.

메모리 할당 실패상황을 재연하기 위해 이렇게 만들었습니다. 아무튼, 이 eat_memory 함수는 궁극적으로는 실패하도록 되어있고요, 메모리 할당에 실패하면 newFailHandler 함수를 호출합니다.

newFailHandler 함수가 리턴하면, new operator 는 마지막으로! 메모리 할당을 시도하고, 이번 경우에만 성공합니다.

사실 이 부분이 이해하기가 까다로운데, new operator는 핸들러 함수가 리턴하고 나서 다시 메모리할당을 시도하도록 되어있습니다.

그 이유를 알려면 VS2010 에서는 아래 경로에 있는 new.cpp 파일을 열어서 operator new의 구현을 보면 됩니다.
C:\Program Files\Microsoft Visual Studio 10.0\VC\crt\src\new.cpp

위 코드는 MS의 저작권이 있으므로 직접 보세요 ^^.
코드를 읽어 보면, 주석에 설명된 대로 핸들러를 호출하고 나서 성공적으로 리턴하고 나서, 다시 한번 메모리 할당을 하도록 되어있습니다.

따라서 핸들러 함수의 내부 구현은 메모리 확보가 실패하지 않도록 작성해야 합니다.
1. 핸들러 함수 안에서 std::bad_alloc 을 리턴하게 작성할 수도 있고,
2. 아예 abort() 나 exit() 을 호출하여 프로그램을 종료하도록 작성할 수도 있겠고,
3. 위의 코드에 작성한대로, 글로벌 Flag 변수를 이용하여 더이상 메모리 확보를 요청하지 않도록 할 수도 있습니다.

Advertisements

About sydlife

Lazy programmer, Dad and Husband.
%d bloggers like this: