기본적으로 STL에 제공되는 할당자 클래스다.
하지만 이는 사용자가 임의로 커스텀해서 사용할 수 있다.
커스텀을 위해선 특정 인터페이스를 만족해야 한다.
주의 사항으로는
1. 템플릿으로 만들어야 하며, 템플릿 매개 변수에는 실제로 사용할 객체의 타입 T를 사용한다.
2. 비정적 데이터 멤버를 가질 수 없다.
3. allocate / deallocate 함수를 가져야 하며, allocate의 인자로 객체 T가 필요한 갯수를 넘겨야 한다.
4. 반환하는 포인터는 void*가 아닌 실제 타입의 포인터(T*) 이여야 한다.
cAlloc() = default;
~cAlloc() = default;
allocator도 결국엔 template class 이기 때문에, 생성자와 소멸자를 작성해야하는데, allocator 가 하는 일은 T 타입의 포인터를 할당 해제 해주는 Factory 라고 생각하면 쉽다.
생성자 내에선 해줄 일이 없고, 다른 함수에서 작성한다.
struct rebind {
using other = cAlloc<U>;
};
rebind는 어떤 종류의 객체를 얻기 위해 사용된다.
std::list와 Obj라는 타입이 있을 때, std::list<T, Obj> 할당자는 T를 할당하기 위한 A 이지만, 실제로 내부에서 list는 노드 기반으로 구성되어야 한다. 이렇게 T 타입이 아닌 다른 타입으로도의 할당(node)이 필요해지는데, 이와 같은 요구 사항을 충족하기 위해 rebind를 가져야 17한다. (C++17에서 사용 중지 권고, C++20에서 삭제 예정)
rebind pattern : https://rookiecj.tistory.com/118
관련 stackoverflow : https://stackoverflow.com/questions/54092334/how-to-avoid-rebind-in-allocatort-n-c17
template<class _Value_type,
class _Voidptr>
struct _List_node
{ // list node
using _Nodeptr = _Rebind_pointer_t<_Voidptr, _List_node>;
_Nodeptr _Next; // successor node, or first element if head
_Nodeptr _Prev; // predecessor node, or last element if head
_Value_type _Myval; // the stored value, unused if head
//중략
결과적으로 list<T>는 rebind<_List_node>::other를 참조함으로써, T 객체의 할당자를 통해 위의 List_node를 찾게 된다.
( 자세한 설명은 https://rookiecj.tistory.com/118 이곳을 보자 )
( 사실 C++17 부턴, rebind가 사용중지 권고가 내려져 정의하지않아도 컴파일은 잘 된다. )
template <typename U>
cAlloc(const cAlloc<U>& other) { }
다른 타입 (U) 에 대한 복사 생성자를 정의해준다.
pointer allocate(size_type ObjectNum, const_void_pointer hint) {
allocate(ObjectNum);
}
pointer allocate(size_type ObjectNum) {
return static_cast<pointer>(operator new(sizeof(T) * ObjectNum));
}
초기화 되지 않은 곳의 n * sizeof(T) 바이트 만큼을 할당합니다.
지역참조를 위해 hint 라는 포인터를 사용할 수 있습니다. ( 구현자가 지원하는 경우 가깝에 만드려고 시도할 수 있음 )
( const_void_pointer 를 받는 allocate는 C++17부터 사용중지 권고가 내려졌다. )
void deallocate(pointer p, size_type ObjectNum) {
operator delete(p);
}
pointer에 의해 참조되는 곳을 할당 해제한다. deallocate가 삭제하는 곳은 꼭 allocate가 할당한 저장소여야 한다.
size_type max_size() const { return std::numeric_limits<size_type>::max() / sizeof(value_type);
이 container가 사용할 수 있는 최대 용량
template<typename U, typename... Args>
void construct(U *p, Args&& ...args) {
new(p) U(std::forward<Args>(args)...);
}
template <typename U>
void destroy(U *p) {
p->~U();
}
실제로 allocate 된 곳에 새로운 형태의 오브젝트를 생성하는 construct와 destroy 함수
construct 와 allocate는 container 의 reserve와 resize 를 생각하면 이해하기 쉬움
reserve : 생성자를 호출하지 않고 사이즈 만큼 공간을 예약 ( allocate )만 해둠
resize : 사이즈만큼 공간을 할당 ( allocate ) 하고, 사이즈만큼 생성자 ( construct ) 를 호출함
#pragma once
#include <limits>
template <typename T>
class cAlloc {
public:
using value_type = T;
using pointer = T * ;
using const_pointer = const T*;
using void_pointer = void*;
using const_void_pointer = const void*;
using size_type = size_t;
using difference_type = std::ptrdiff_t;
cAlloc() = default;
~cAlloc() = default;
template <typename U>
struct rebind {
using other = cAlloc<U>;
};
template <typename U>
cAlloc(const cAlloc<U>& other) { }
pointer allocate(size_type ObjectNum, const_void_pointer hint) {
allocate(ObjectNum);
}
pointer allocate(size_type ObjectNum) {
return static_cast<pointer>(operator new(sizeof(T) * ObjectNum));
}
void deallocate(pointer p, size_type ObjectNum) {
operator delete(p);
}
size_type max_size() const { return std::numeric_limits<size_type>::max() / sizeof(value_type); }
template<typename U, typename... Args>
void construct(U *p, Args&& ...args) {
new(p) U(std::forward<Args>(args)...);
}
template <typename U>
void destroy(U *p) {
p->~U();
}
};
https://help.perforce.com/sourcepro/11.1/html/toolsug/11-6.html
https://openmynotepad.tistory.com/58?category=854742
https://www.youtube.com/watch?v=pP15kDeXJU0
https://www.youtube.com/watch?v=kSWfushlvB8
https://jacking75.github.io/Cpp_EASTL/
https://woo-dev.tistory.com/51
'프로그래밍 > Morden C++' 카테고리의 다른 글
smart pointer 삭제자 지정 (0) | 2022.01.27 |
---|---|
시간 관련 (0) | 2022.01.17 |
std::min, std::max 사용시 (0) | 2021.10.06 |
std::map (0) | 2021.10.05 |
std::clamp (0) | 2021.09.29 |