27 Şubat 2018 Salı

std::allocator

Giriş
Allocator STL'in en az bilinen ve kullanılan özelliklerinden bir tanesi. Bu sınıf tüm STL veri yapıları ile kullanılır. Açıklaması şöyle
The std::allocator class template is the default Allocator used by all standard library containers if no user-specified allocator is provided.
Bu sınıf heap'ten bellek alır. Açıklaması şöyle
Allocates n * sizeof(T) bytes of uninitialized storage by calling ::operator new(std::size_t)

State
C++11'den önce allocator nesneleri state tutmazdı. Artık tutabiliyor.

Allocator Bellek Yönetimi Ve Nesne Kurulumunun Birbirinden Ayrılmasını Sağlar
Kod ile örmek vermek gerekirse
std::vector<X> v;
v.reserve(4);        // (1)
v.push_back( X{} );  // (2)
v.push_back( X{} );  // (3)
v.clear();           // (4)
1. allocator ile bellek ayrılmasını sağlar.
2 ve 3 ile nesneler ayrılan bellek alanına kurulur
4 ile nesneler yok edilir ancak allocator ile ayrılan bellek alanı korunur.
En sonunda vector yok olduğu için bellek alanı iade edilir.

Copy Constructor
Allocator bir şekilde copy constructor metodunu desteklemeli ama niçin anlamadım.

Allocator içinde bazı typedefler kullanılır.
Bunlar pointer ve const_pointer tipleridir.
template <typename T>
class my_allocator
{
public:
  typedef size_t size_type;
  typedef T* pointer;
  typedef const T* const_pointer;

}
allocate metodu
Malloc gibi çalışır. Her allocator haliyle bir allocate metoduna sahip olmalı.
 İmzası şöyle
pointer allocate(size_type n){
...
}
veya şöyle. İkinci imza tam olarak ne işe yarıyor bilmiyorum.
pointer allocate(size_type num, const_pointer = 0) {
...
}
Örnek
C++11 ile şöyle yaparız.
template <typename T>
struct aligned_allocator {
  typedef T value_type

  T* allocate(std::size_t num) {
    void* ptr = nullptr;
    if (posix_memalign(&ptr,4096,num*sizeof(T)))
      throw std::bad_alloc();
    return reinterpret_cast<T*>(ptr);
  }

  void deallocate(T* p, std::size_t num) {
    free(p);
  }
};


std::vector<int16_t,aligned_allocator<int16_t>> v(64);
std::vector<uint16_t,aligned_allocator<uint16_t>> v(64);
std::vector<int16_t,aligned_allocator<int16_t>> v (64);
deallocate metodu
Açıklaması şöyle.
void deallocate( T* p, std::size_t n );
"The argument n must be equal to the first argument of the call to allocate() that originally produced p; otherwise, the behavior is undefined."
Free gibi çalışır. Her allocator haliyle bir deallocate metoduna sahip olmalı.İmzası şöyle
// deallocate memory at 'p'
void deallocate(pointer p, size_type num=0) { 
...
}
max_size
Allocator'ın en fazla ne kadarlık bellek ayırabileceğini gösterir.
Returns the largest value that can be passed meaningfully to allocate() to allocate storage
Şöyle yaparız
size_type max_size(){
  return 1;
}
operator != metodu
Bu metod bir şekilde sağlanmalı. Sebebini bilmiyorum. Açıklaması şöyle.
You are missing !=, ==, cross-type implicit conversion, and, as pertinent here, rebind.
Yani şu allocator sınıfı yeterli değil.
template<typename Tag, typename T>
struct MyAllocator
{
  using value_type = typename std::allocator<T>::value_type;

  T* allocate(std::size_t n)
  {
    Tag::logAllocation();
    return std::allocator<T>{}.allocate(n);
  }

  void deallocate(T* p, std::size_t n)
  {
    Tag::logDeallocation();
    std::allocator<T>{}.deallocate(p, n);
  }
};
rebind metodu
C++11 ile bu metod isteğe bağlı hale geldi. Bu metodu sağlamazsak allocator_traits bir metod sağlar.
Örnek
Şöyle yaparız.
typedef typename _Alloc::template rebind<_Ty>::other _Alty;
Örnek
Elimizde şöyle bir yapı olsun. void allocator char allocator olarak rebind edilir.
template <typename Alloc = std::allocator<void> >
struct C {
  using allocator_type = Alloc;
  bip::vector<char, typename Alloc::template rebind<char>::other> data;

  C(Alloc alloc = {}) : data(alloc) {}

  void add_char() {
    data.push_back('x');
  }
};
STL sınıfları ile kullanımı
Şöyle kullanılır.
std::vector<T,tbb::scalable_allocator<T> >
Boost ile gelen bir sürü allocator da var. Bunlardan bir tanesi aligned_allocator. Şöyle kullanılır. Her bir eleman arasına 16 byte boşluk ekler.
#include <boost/align/aligned_allocator.hpp>
#include <vector>
int main()
{
  std::vector<int, boost::alignment::
    aligned_allocator<int, 16> > v(100);
}