«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Archives
Today
Total
11-21 18:21
관리 메뉴

lancelot.com

allocator - policy base design 본문

프로그래밍

allocator - policy base design

lancelot50 2022. 7. 27. 20:54
  • allocator
    • 메모리 할당관련 함수를 추상화한 도구
    • 메모리 할당 방식을 쉽게 변경할 수 있게 해준다.
  • std::allocator
    • C++ 표준 메모리 할당기
    • 기본 구현은 operator new() / operator delete() 사용
  • allocator 멤버함수 - 사용자 정의 allocator를 만들기위해서는 아래 멤버함수 구현 필요
    • allocate : 메모리 할당
    • construct : 생성자 호출
    • destroy : 소멸자 호출
    • deallocate 메모리 해제
  •  사용자정의 allocator를 STL에 전달하려면
    • default constructor 
    • template constructor
    • value_type member
    • == 연산, != 연산 이 가능해야한다
#include<iostream>
#include"point.h"

template<typename T>
class MyAlloc
{
public :
	T* allocate(std::size_t n)
	{
		T* p = static_cast<T*>(std::malloc(n * sizeof(T)));
		std::cout << "allocate" << std::endl;
		return p;
	}
	void deallocate(T* p, std::size_t n) noexcept
	{
		free(p);
		std::cout << "deallocate" << std::endl;
	}
	template<typename ... ARGS>
	void construct(T* p, ARGS&& ... args)
	{
		new(p) T(std::forward<ARGS>(args)...);
		std::cout << "MyAlloc constructor" << std::endl;
	}
	void destroy(T* p)
	{
		p->~T();
		std::cout << "MyAlloc destroy" << std::endl;
	}
};

int main()
{
	// 아래처럼 new/delete 를 사용하는 코드가 10곳에 있는데, 
	// 메모리 할당해지 방식을 변경하고 싶다면?
	//  -> 10곳 모두 수정해야한다.
	Point* p1 = new Point(1, 2);
	delete p1;

	// 대신 allocator 라는 메모리할당자를 사용한다
//	std::allocator<Point> ax;
	MyAlloc<Point> ax;
	// 와 같이 해서 allocator를 쉽게 변경할 수 있다.

	Point* p2 = ax.allocate(1);
	ax.construct(p2, 0, 0);
	ax.destroy(p2);
	ax.deallocate(p2, 1);
}

 

  • std::allocator_traits ( C++11 )
    • construct, destroy 등 allocator 관련 함수의 기본 구현을 제공
    • 사용자 정의 allocator 안에 construct / destroy 함수가
      • 있는 경우 : 사용자가 만든 construct 함수 사용
      • 없는 경우 : allocator_traits 가 디폴트 구현 제공
  • std::allocator 
    • C++20 이전 : allocator / deallocator / construct / destroy 모두 제공
    • C++20 부터 :  construct / destroy 함수 제공 안함
template<typename T>
class MyAlloc
{
	...
    using value_type=T;	// allocator_traits<> 를 사용하려면 필요
}


int main()
{
	Point* p1 = ax.allocate(1);
	ax.construct(p1, 0, 0);
	ax.destroy(p1);
	ax.deallocate(p1, 1);

	// allocator::traits<> 사용
	Point* p2 = std::allocator_traits < MyAlloc<Point>>::allocate(ax, 1);
	std::allocator_traits<MyAlloc<Point>>::construct(ax, p2, 0, 0);
	std::allocator_traits<MyAlloc<Point>>::destroy(ax, p2);
	std::allocator_traits<MyAlloc<Point>>::deallocate(ax, p2, 1);
}

 

  • MyAlloc 을 제작한 Vector에 적용
#include<iostream>
#include"point.h"
#include"MyAlloc.h"

template<typename T, typename Alloc=std::allocator<T> >
class Vector
{
	T* buff;
	std::size_t size;
	std::size_t capacity;

	Alloc ax;
public :
	Vector(std::size_t sz, const T& value) : size(sz), capacity(sz)
	{
// memroy allocation
//		buff = new T[sz];
//		buff = static_cast<T*>(operator new(sizeof(T)*sz));
		buff = std::allocator_traits<Alloc>::allocate( ax, sizeof(T)*sz );

// construction
		int i = 0;
		try
		{
			for (i = 0; i < sz; ++i)
			{
//				new(&buff[i]) T;	// default constructor
//				new(&buff[i]) T(value); // copy constructor
				std::allocator_traits<Alloc>::construct(ax, &buff[i], value);
			}
		}
		catch (...)
		{
			for (int j = i - 1; j >= 0; --j)
				//				buff[j].~T();
				std::allocator_traits<Alloc>::destroy(ax, &buff[i]);

//			operator delete(buff);
			std::allocator_traits<Alloc>::deallocate(ax, buff, capacity);
			size = 0;
			capacity = 0;

			throw;
		}
	}
	~Vector()
	{
//		delete[] buff;

		for (__int64 j = size - 1; j >= 0; --j)
//			buff[j].~T();
			std::allocator_traits<Alloc>::destroy(ax, &buff[j]);
//		operator delete(buff);
		std::allocator_traits<Alloc>::deallocate(ax, buff, capacity);
	}
};

int main()
{
	Point pt(0, 0);
	Vector<Point> v(10, pt);

	Vector<Point, MyAlloc<Point>> v2(10, pt);

	return 0;
}

 

  • STL 에 전달을 위한 4가지 조건이 적용된 최종 MyAlloc
template<typename T>
struct MyAlloc
{
	T* allocate(std::size_t n)
	{
		T* p = static_cast<T*>(std::malloc(n * sizeof(T)));
		std::cout << "allocate" << std::endl;
		return p;
	}
	void deallocate(T* p, std::size_t n) noexcept
	{
		free(p);
		std::cout << "deallocate" << std::endl;
	}
	template<typename ... ARGS>
	void construct(T* p, ARGS&& ... args)
	{
		new(p) T(std::forward<ARGS>(args)...);
		std::cout << "MyAlloc constructor" << std::endl;
	}
	void destroy(T* p)
	{
		p->~T();
		std::cout << "MyAlloc destroy" << std::endl;
	}
	using value_type = T;

	// template constructor
	template<typename U>
	MyAlloc(const MyAlloc<U>&) noexcept {}

	// template constructor 가 있으니 default constructor 가 자동생성이 안되므로 만들어준다
	MyAlloc() = default;
};

// ==
template<typename T, typename U>
bool operator==(const MyAlloc<T>&, const MyAlloc<U>&) { return true; }
// !=
template<typename T, typename U>
bool operator!=(const MyAlloc<T>&, const MyAlloc<U>&) { return false; }