operator new, delete 사용자 정의버전의 요구사항을 살펴보자.
1. operator new 요구사항
1) 반환값
- 메모리 할당 가능? 해당 메모리에 대한 포인터 반환 : bad_alloc 타입의 예외;
2) 가용메모리 부족?
- 메모리 부족시 new처리자 함수호출하여 2회이상 메모리할당 재시도한다.
- operator new가 예외를 던지는 경우 : new처리자 함수에 대한 포인터가 null일때만
3) 크기가 없는 메모리 요청에 대한 대비책 필요
- 0바이트 메모리 요청시 1바이트 요구로 간주하여 처리한다.
- (0바이트 요구는 거의없다고 보면된다..)
4) "기본(normal)" 형태의 new가 가려지지 않도록
- 이건 차후 설명하겠다.
void * operator new(std::size_t size) throw (std::bad_alloc)
{
using namespace std;
if(size == 0) { // 3번항목
size = 1;
}
while(true) {
size 바이트를 할당해봅니다;
if(할당 성공)
return (할당된 메모리에 대한 포인터) ;
//2번항목 -> 할당실패시, 현재의 new처리자 함수가
//어느 것으로 설정되어 있는지 찾아낸다.(아래를보세요)
new_handler globalHandler = set_new_handler(0); //set_new_handler는 이전 new 핸드러를 반환
set_new_handler(globalHandler);
if(globalHandler) (*globalHandler)();
else throw std::bad_alloc(); // 1번항목
}
}
*풀이
- while(true) : 이 루프를 빠져나오는 조건은, (1) 메모리 할당이 성공하든지, (2) new처리자 함수쪽에서 해결해주든지 이다. (항목 49.참조)
- operator new 멤버 함수는 상속이 되는 함수이다. 위 함수의 매개변수는 size이다. 즉 이 멤버변수는 해당 특정 클래스 '만'을 위한 operator new 함수이다. 만약 해당클래스를 상속받은 파생클래스가 operator new 호출시, 문제가 생긴다. 아래 예시를 보자.
class Base{
public :
static void * operator new (std::size_t size) throw(std::bad_alloc);
...
};
class Derived : public Base // Derived에는 operator new가 선언되지 않았음
{ ... };
Derived *p = new Derived; // Base :: operator new가 호출!
*풀이
- 만약 Base클래스의 operator new가 이런 상황에 대해 조치를 취하지 않았다면 문제가 생긴다. 전체 설계를 바꾸지 않고 operator new만 바꿔서 해결하는 방법을 살펴보자.
static void * operator new (std::size_t size) throw(std::bad_alloc)
{
if (size != sizeof(Base)) // "틀린" 크기가 들어오면, 표준 operator new 쪽에서 처리하도록 넘긴다.
return :: operator new(size);
... // "맞는" 크기가 들어오면 여기서 처리한다.
}
*풀이
- 위 "1. - 4) 기본형태의 new가 가려지지 않도록한다." 를 보여주는 의사코드이다.
2. operator delete
class Base { // 이전과 같으나, 지금은 operator delete가 추가된 상태
public :
static void * operator new(std::size_t size) thro(std::bad_alloc);
static void operator delete(void *rawMemory, std::size_t size) throw();
...
};
void Base::operator delete(void *rawMemory, std::siz_t size) throw()
{
if(rawMemory == 0) return; // 널 포인터에 대한 점검
if(size != sizeof(Base)) { // 크기가 "틀린" 경우, 표준 operator delete가 처리하도록 넘긴다.
::operator delete(rawMemory);
return;
}
rawMemory가 가리키는 메모리를 해제한다.
return;
}
*풀이
- operator new와 거의 동일하나, 해제 포인터가 null 일경우 아무것도 하지 않고 return한다.
- 그리고 Base 사이즈와 다를 경우, 표준 operator delete가 처리하도록 권한을 넘긴다.
이것만은 잊지 말자!
- 관례적으로, operator new 함수는 메모리 할당을 반복해서 시도하는 무한 루프를 가져야 하고, 메모리 할당 요구를 만족시킬 수 없을때 new 처리자를 호출해야하며, 0바이트에 대한 대책도 필요하다. 클래스 전용 버전은 자신이 할당하기로 예정된 크기보다 더 큰(틀린)메모리 블록에 대한 요구도 처리해야 한다.
- operator delete함수는 널 포인터가 들어왔을 때 아무일도 하지 않아야 한다. 클래스전용버전의 경우에는 예정 크기보다 더 큰 블록을 처리해야한다.
'책 정리 > Effective C++ 3rd' 카테고리의 다른 글
항목 53. 컴파일러 경고를 지나치지 말자. (0) | 2020.05.12 |
---|---|
항목 52. 위치지정 new를 작성한다면 위치치정 delete도 같이 준비하자 (0) | 2020.05.12 |
항목 50. new a및 delete를 언제 바꿔야 좋은 소리를 들을지를 파악해 두자 (0) | 2020.04.01 |
항목 49. new 처리자의 동작원리를 제대로 이해하자 (0) | 2020.04.01 |
항목 48. 템플릿 메타프로그래밍, 하지 않겠는가? (0) | 2020.03.27 |
댓글