[C++][Effective C++] 항목 49 : new 처리자의 동작 원리를 제대로 이해하자 (하)

WestCoast·2022년 5월 9일
0

C, C++

목록 보기
12/12

5. 객체의 클래스 타입에 따른 new 처리자 구현


#include <iostream>
using namespace std;

#define SIZE 499999999L

class NewHandlerHolder
{
public:
    explicit NewHandlerHolder(new_handler nh)
        : handler(nh) // 4. handler(OutOfMemDefalut)
    {
    }

    ~NewHandlerHolder()
    {
        // 6. 소멸될 때 이전 new 처리자(OutOfMemDefalut)를 복귀시킨다.
        std::set_new_handler(handler);
    }

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

class Widget
{
public:
    static std::new_handler SetNewHandler(std::new_handler p) throw()
    {
        new_handler oldHandler = currentHandler;
        currentHandler = p;
        return oldHandler;
    }

    static void *operator new(size_t size) throw(bad_alloc)
    {
        // 1. std::set_new_handler에 currentHandler(OutOfMem)를 설치한다.
        // 2. std::set_new_handler 가 원래 가지고 있던 OutOfMemDefalult를 반환한다.
        std::new_handler nh = std::set_new_handler(currentHandler);
        // 3. NewHandlerHolder에 OutOfMemDefalut를 기억시킨다.
        NewHandlerHolder h(nh);

        // 5. OutOfMem 함수가 호출된다.
        return ::operator new(size);
    }

private:
    // Widget을 위한 특별한 new 처리자
    // 정적으로 선언한다.
    static std::new_handler currentHandler;
    int arr[SIZE];
};

std::new_handler Widget::currentHandler = nullptr;

void OutOfMem()
{
    cerr << "Widget 할당 에러" << endl;
    throw std::bad_alloc();
}

void OutOfMemDefault()
{
    cerr << "일반적인 할당 에러" << endl;
    throw std::bad_alloc();
}

int main()
{
	// 전역 new 처리자 설치
    std::set_new_handler(OutOfMemDefault);
    // Widget 클래스의 new 처리자 설치
    Widget::SetNewHandler(OutOfMem);

    try
    {
        // OutOfMem 함수가 호출된다.
        Widget *w = new Widget();
    }
    catch (std::exception ex)
    {
        cout << ex.what() << endl;
    }

    try
    {
        // OutOfMemDefault 함수가 호출된다.
        int *arr = new int[SIZE];
    }
    catch (std::exception ex)
    {
        cout << ex.what() << endl;
    }
}

출력


6. 클래스 템플릿을 활용한 일반화된 new 처리자 클래스 구현


#include <iostream>
using namespace std;

#define SIZE 499999999L

// 이전 new 처리자를 기억하고 있을 클래스
class NewHandlerHolder
{
public:
	explicit NewHandlerHolder(new_handler nh)
		: handler(nh)
	{ }

	~NewHandlerHolder()
	{
		std::set_new_handler(handler);
	}

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

template<typename T>
class NewHandlerSupport
{
public:
	static std::new_handler SetNewHandler(std::new_handler p) throw()
	{
		std::new_handler oldHandler = currentHandler;
		currentHandler = p;
		return oldHandler;
	}

	static void* operator new(size_t size) throw(bad_alloc)
	{
		NewHandlerHolder h(std::set_new_handler(currentHandler));
		return ::operator new(size);
	}

private:
	// staticr으로 선언된 멤버데이터는 T 타입에 따라 사본이 생성된다.
	static std::new_handler currentHandler;
};

template<typename T>
std::new_handler NewHandlerSupport<T>::currentHandler = nullptr;


// 우리가 실제로 사용할 클래스는 그저 상속만 받으면 된다.
class Widget : public NewHandlerSupport<Widget>
{
	// 에러처리를 위한 구문들이 이제 전부 기본클래스인 
	// NewHandlerSupport 클래스로 올라갔다.

private:
	int arr[SIZE];
};

void OutOfMem()
{
	cerr << "Widget 할당 에러" << endl;
	throw std::bad_alloc();
}

void OutOfMemDefault()
{
	cerr << "일반적인 할당 에러" << endl;
	throw std::bad_alloc();
}

int main()
{
	std::set_new_handler(OutOfMemDefault);
	Widget::SetNewHandler(OutOfMem);

	try
	{
		// OutOfMem 함수가 호출된다.
		Widget* w = new Widget();
	}
	catch (std::exception ex)
	{
		cout << ex.what() << endl;
	}

	try
	{
		// OutOfMemDefault 함수가 호출된다.
		int* arr = new int[SIZE];
	}
	catch (std::exception ex)
	{
		cout << ex.what() << endl;
	}
}

profile
게임... 만들지 않겠는가..

0개의 댓글