박오이님
무미건조한 개발자
박오이님
전체 방문자
오늘
어제
  • 뭥미 (101)
    • 프로젝트 (8)
      • 자가 보호 (3)
      • 주식 시장 분석 도구 (5)
    • 보안 (7)
      • 개론 (2)
      • 웹 (2)
      • 시스템 (2)
    • 개발 (69)
      • C++ (12)
      • Win32 (7)
      • MFC (2)
      • 자료구조 (8)
      • 알고리즘 (22)
      • 백준 (9)
      • 프로그래머스 (4)
      • LeetCode (0)
      • 개발자 면접 준비 (4)
      • OpenGL (1)
    • 서적 (13)
      • Effective C++ (9)
      • Effective Modern C++ (4)
    • 관심사 (4)
      • 재테크 (4)

블로그 메뉴

  • 홈
  • 방명록

공지사항

인기 글

태그

  • C
  • 안경잡이개발자
  • 에라토스테네스의 체 #C #C++ #개발 #알고리즘 #BFS #DFS #백준 #백준알고리즘
  • CPP
  • EffectiveC++
  • 알고리즘
  • 프로세스메모리
  • C++
  • 개발
  • 나동빈
  • 시스템프로그래밍
  • 나동빈 #알고리즘 #동빈나
  • std
  • 합집합찾기
  • 최소간선비용
  • 윈도우
  • 코딩컨벤션
  • 윈도우프로그래밍
  • Functional
  • jsoncpp
  • DFS #BFS #알고리즘 #프로그래밍 #코딩테스트 #코딩 #C++ #C
  • 크루스칼알고리즘
  • 에라토스테네스의 체 #알고리즘 #개발 #C #C++ #소수 #소수판별
  • 플로이드와샬알고리즘 #최단경로 #백준 #알고리즘 #개발 #C #C++
  • 동빈나
  • vcpkg
  • 윈도우개발자
  • 백준 #알고리즘 #플로이드와샬 #DFS #BFS #C #C++
  • JSON
  • 윈도우시스템프로그래밍

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
박오이님

무미건조한 개발자

서적/Effective C++

항목 51: new 및 delete를 작성할 때 따라야 할 기존의 관례를 잘 알아 두자

2022. 7. 26. 01:24

기존의 관례에 잘 맞는 operator new를 구현하려면 다음의 요구사항만큼은 기본으로 지켜야 합니다.
- 반환 값이 제대로 되어 있어야 합니다.
- 가용 메모리가 부족할 경우에는 new 처리자 함수를 호출(항목 49)
- 크기가 없는 (0byte) 메모리 요청에 대한 대비책
- 기본 형태의 new가 가려지지 않도록 해야합니다.

void* operator new(std::size_t size) throw(std::bad_alloc)
{
	using namespace std;
    
    if(size == 0)
    {
    	size = 1;
	}
    
    while (true)
    {
    	//	size 바이트를 할당
        if(/*할당 성공*/)
        {
        	return (/*할당된 메모리에 대한 포인터*/);
        }
        
        //	할당 실패 시, 현재 new 처리자 함수가
        //	어느 것으로 설정되어 있는지 찾아냅니다.
        new_handler globalHandler = set_new_handler(0);
        set_new_handler(globalHandler);
        
        if(globalHandler) (*globalHandler)();
        else throw std::bad_alloc();
    }
}

위 방법은 단일 스레드 환경이라면, 문제 없습니다.
다중스레드 환경에서는 new 처리자 함수를 둘러싼 자료구조들이 조작될 때 스레드 안전성이 보장되어야 하기 때문에 스레드 잠금을 걸어야합니다.

그리고 operator new 함수에는 무한 루프 가능성이 있습니다.
이 루프를 빠져나오는 조건은 메모리 할당이 성공 / 항목 49에서 이야기한 동작들 중 한 가지를 new 처리자 함수 쪽에서 제공
- 가용 메모리를 증가
- 다른 new 처리자를 설치
- new 처리자의 설치를 제거
- bad_alloc 혹은 bad_alloc에서 파생된 타입의 예외를 던짐.
- 프로그램 중단
반드시 해주지 않으면, operator new 내 무한 루프에 빠질 수 있음.

operator new 멤버 함수는 파생 클래스 쪽으로 상속이 되는 함수 -> 이 부분을 꼭 고려!

특정 클래스
전용의 할당자를 만들어서 할당 효율을 최적화하기 위해서 사용자 정의 메모리 관리자를 작성할 수 있음(항목50)
어떤 X라는 클래스를 위한 operator new 함수가 잇다면, 이 함수의 동작은 크기가 sizeof(X)인 객체에 대해 맞추어져 있는 것
상속 때문에 파생 클래스 객체를 담을 메모리를 할당하는 데 기본 클래스의 operator new 함수가 호출되는 경우가 발생

class Base
{
public:
	static void* operator new(std::size_t) throw(std::bad_alloc);
    ...
};

class Derived: public Base		//	Derived에서는 operator new는 없음.
{...};

Derived *p = new Derived;		//	Base::operator new가 호출됨.
								
void* Base::operator new(std::siz_t size) throw(std::bad_alloc)
{
	if(size != sizeof(Base))			//	의도된 크기가 들어오지 않으면, 표준 operator new 쪽으로 메모리 할당 처리하도록 넘깁니다.
    	return ::operator new(size);	
        
	...
}

C++ 에는 모든 독립 구조의 객체는 반드시 크기가 0이 넘습니다. 그래서 sizeof(Base)는 0이 절대 될 수 없습니다,

operator new[]를 직접 구현하는 것은 어렵습니다. operator new[] 안에서 해 줄 일은 단순히 원시 메모리의 덩어리를 할당하는 것

첫째, 객체 하나가 얼마나 큰지를 확정할 방법이 없습니다.
상속 때문에 파생 클래스 객체의 배열을 할당하는 데 기본 클래스의 operator new[] 함수가 호출될 수 있습니다.
그리고 파생 클래스 객체는 대체적으로 기본 클래스 객체보다 더 큽니다.
그래서 Base::operator new[]안에서조차도 배열에 들어가는 객체 하나의 크기가 sizeof(Base)라는 가정을 할 수 없음

둘째, size_t 타입의 인자는 객체들을 담기에 딱 맞는 메모리 양보다 더 많게 설정되어 있을 수도 있습니다.
동적으로 할당된 배열에는 배열 원소의 개수를 담기 위한 자투리 공간이 추가로 들어가는 경우가 있음.

operator delete를 작성할 때의 관례
C++는 널 포인터에 대한 delete 적용이 항상 안전하도록 보장한다는 사실만 잊지 않으면 됩니다.

class Base
{
public:
	static void* operator new(std::size_t) throw(std::bad_alloc);
    static void operator delete(void *rawMemory, std::size_t size) throw();
    ...
};
								
void Base::operator delete(void *rawMemory, std::size_t size) throw()
{
	if(rawmemory == 0) return;			//	널 포인터 점검
    
	if(size != sizeof(Base))			//	의도된 크기가 들어오지 않으면, 
    									//	표준 operator delete 쪽으로 메모리 제거 하도록 넘깁니다.
    {
    	::operator delete(rawMemory);	//
        return;
	}
	...
}

가상 소멸자가 없는 기본 클래스로부터 파생된 클래스의 객체를 삭제하려고 할 경우에는 operator delete로 C++가 넘기는 size_t 값이 엉터리일 수 있음. 기본 클래스에 가상 소멸자를 꼭 두어야 하는 이유 중 하나

저작자표시 (새창열림)

'서적 > Effective C++' 카테고리의 다른 글

항목 49: new 처리자의 동작 원리를 제대로 이해하자  (0) 2022.07.18
항목 36: 상속 받은 비가상 함수를 파생 클래스에서 재정의하는 것은 금물!  (0) 2022.04.07
항목 35: 가상함수 대신 쓸 것들도 생각해 두는 자세를 시시때때로 길러 두자  (0) 2022.04.07
항목 34: 인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하자  (0) 2022.04.07
항목 33: 상속된 이름을 숨기는 일은 피하자  (0) 2022.04.07
    '서적/Effective C++' 카테고리의 다른 글
    • 항목 49: new 처리자의 동작 원리를 제대로 이해하자
    • 항목 36: 상속 받은 비가상 함수를 파생 클래스에서 재정의하는 것은 금물!
    • 항목 35: 가상함수 대신 쓸 것들도 생각해 두는 자세를 시시때때로 길러 두자
    • 항목 34: 인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하자
    박오이님
    박오이님
    긍정도 아니고 부정도 아닌 0

    티스토리툴바