Собственный аллокатор с задаваемым выделением памяти
От: avovana Россия  
Дата: 26.03.18 20:18
Оценка:
Дорогие форумчане!

Честно говоря, не думал, что пообщаюсь в живую с мастодонтами своего дела.
А именно такими мне казались дорогие форумчане, когда что-то искал по теме.
Но, похоже, время пришло.

На cyberforum мне не ответили. Теперь надежда на Вас.

Что было сделано:

Собственный аллокатор, который нормально сработал с std::vector.

Что хочется сделать:

Прокачать аллокатор для использования с std::map(и в будущем для собственного list'a), с имитацией поведения метода reserve(), который есть у std::vector.


I. Пример использования std::vector со своим аллокатором.


Реализация аллокатора:
#ifndef ALLOCATOR_HEADER
#define ALLOCATOR_HEADER
 
#include <cstddef> // size_t
#include <iostream>
#include <memory>
 
template<typename T>
struct AllocatorLogger {
  using size_type = size_t;
  using difference_type = ptrdiff_t;
 
  using value_type = T;
  using pointer = T*;
  using const_pointer = const T*;
  using reference = T&;
  using const_reference = const T&;
/*
  template<typename U>
  struct rebind {
    using other = AllocatorLogger<U>;
  };
*/
  pointer allocate(size_type n) {
    std::cout << __PRETTY_FUNCTION__
      << "[n = " << n << ']' << std::endl;
    void *ptr = malloc(sizeof(value_type) * n);
    if (ptr == nullptr)
      throw std::bad_alloc();
    return reinterpret_cast<pointer>(ptr);
  }
 
  template<typename U, typename... Args>
  void construct(U* p, Args&&... args) {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    new (p) U(std::forward<Args>(args)...);
  }
 
  void destroy(pointer p) {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    p->~T();
  }
 
  void deallocate(pointer p, size_type) {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    free(p);
  }
};
 
#endif // ALLOCATOR_HEADER


Использования аллокатора:
#include <iostream>
#include <cstdlib>
#include <vector>
#include <map>
#include "allocator.h"
 
int main()
{
    auto v = std::vector<int, AllocatorLogger<int>>{};
    
    v.reserve(5);
    
    for(size_t i = 0; i < 5; ++i)
    {
        v.emplace_back(i);
    }
}

Запустить код на wandbox.

Вывод:

T* AllocatorLogger<T>::allocate(AllocatorLogger<T>::size_type) [with T = int; AllocatorLogger<T>::pointer = int*; AllocatorLogger<T>::size_type = long unsigned int][n = 5]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = int; Args = {long unsigned int&}; T = int]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = int; Args = {long unsigned int&}; T = int]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = int; Args = {long unsigned int&}; T = int]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = int; Args = {long unsigned int&}; T = int]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = int; Args = {long unsigned int&}; T = int]
void AllocatorLogger<T>::destroy(AllocatorLogger<T>::pointer) [with T = int; AllocatorLogger<T>::pointer = int*]
void AllocatorLogger<T>::destroy(AllocatorLogger<T>::pointer) [with T = int; AllocatorLogger<T>::pointer = int*]
void AllocatorLogger<T>::destroy(AllocatorLogger<T>::pointer) [with T = int; AllocatorLogger<T>::pointer = int*]
void AllocatorLogger<T>::destroy(AllocatorLogger<T>::pointer) [with T = int; AllocatorLogger<T>::pointer = int*]
void AllocatorLogger<T>::destroy(AllocatorLogger<T>::pointer) [with T = int; AllocatorLogger<T>::pointer = int*]
void AllocatorLogger<T>::deallocate(AllocatorLogger<T>::pointer, AllocatorLogger<T>::size_type) [with T = int; AllocatorLogger<T>::pointer = int*; AllocatorLogger<T>::size_type = long unsigned int]

Здесь получаем, что хотим — 1 вызов аллокатора с выделением места для последующего конструирования 5ти элементов.

II. Пример использования std::map со своим аллокатором.


Хочется такого же поведения и в связке "аллокатор + std::map":

int main()
{
    auto m = std::map<int, int, std::less<int>, AllocatorLogger<std::pair<const int, int>>>{};
    
    for(size_t i = 0; i < 5; ++i)
    {
        m[i] = i;
    }
}


Но здесь получаем вывод:
 AllocatorLogger<T>::allocate(AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int][n = 1]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = std::pair<const int, int>; Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; T = std::_Rb_tree_node<std::pair<const int, int> >]
T* AllocatorLogger<T>::allocate(AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int][n = 1]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = std::pair<const int, int>; Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; T = std::_Rb_tree_node<std::pair<const int, int> >]
T* AllocatorLogger<T>::allocate(AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int][n = 1]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = std::pair<const int, int>; Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; T = std::_Rb_tree_node<std::pair<const int, int> >]
T* AllocatorLogger<T>::allocate(AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int][n = 1]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = std::pair<const int, int>; Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; T = std::_Rb_tree_node<std::pair<const int, int> >]
T* AllocatorLogger<T>::allocate(AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int][n = 1]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = std::pair<const int, int>; Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; T = std::_Rb_tree_node<std::pair<const int, int> >]
void AllocatorLogger<T>::deallocate(AllocatorLogger<T>::pointer, AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int]
void AllocatorLogger<T>::deallocate(AllocatorLogger<T>::pointer, AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int]
void AllocatorLogger<T>::deallocate(AllocatorLogger<T>::pointer, AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int]
void AllocatorLogger<T>::deallocate(AllocatorLogger<T>::pointer, AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int]
void AllocatorLogger<T>::deallocate(AllocatorLogger<T>::pointer, AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int]

Т.е. 5 вызовов аллокатора.

III. Попытка задания аллокации.

Вот что я пробовал, но у меня не получилось.

Исправление в аллокаторе. Добавление нового параметра int Size:
template<typename T, int Size>
struct AllocatorLogger {
 
  pointer allocate(size_type n) {
    std::cout << __PRETTY_FUNCTION__
      << "[n = " << n << ']' << std::endl;
    void *ptr = malloc(sizeof(value_type) * n * Size);
    if (ptr == nullptr)
      throw std::bad_alloc();
    return reinterpret_cast<pointer>(ptr);
  }
 
  //...
}


Вызов c заданием параметра — 5:
int main()
{
    auto m = std::map<int, int, std::less<int>, AllocatorLogger<std::pair<const int, int>, 5>>{};
    
    for(size_t i = 0; i < 5; ++i)
    {
        m[i] = i;
    }
}

Но... ругается. Не пойму, что ему надо. Помогите, пожалуйста, разобраться.
Re: Собственный аллокатор с задаваемым выделением памяти
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 26.03.18 20:30
Оценка:
Здравствуйте, avovana, Вы писали:

A>Дорогие форумчане!


И тебе привет, дорогой


A>Честно говоря, не думал, что пообщаюсь в живую с мастодонтами своего дела.


Мастодонты вымерли примерно тогда же, когда и мамонты, а тут пока еще все живы, и отвечают


A>А именно такими мне казались дорогие форумчане, когда что-то искал по теме.

A>Но, похоже, время пришло.

Всё когда-то бывает в первый раз


A>

Что было сделано:

A>Собственный аллокатор, который нормально сработал с std::vector.

A>

Что хочется сделать:

A>Прокачать аллокатор для использования с std::map(и в будущем для собственного list'a), с имитацией поведения метода reserve(), который есть у std::vector.

А в чем проблема? Я, допустим, аллокаторы давно писал, и проблематики на вскидку не помню. Вроде все работало



A>Вызов c заданием параметра — 5:

A>
A>int main()
A>{
A>    auto m = std::map<int, int, std::less<int>, AllocatorLogger<std::pair<const int, int>, 5>>{};
    
A>    for(size_t i = 0; i < 5; ++i)
A>    {
A>        m[i] = i;
A>    }
A>}
A>

A>Но... ругается. Не пойму, что ему надо. Помогите, пожалуйста, разобраться.


На что ругается-то?
По идее, аллокатор для map вызывается для аллокации памяти для каждого узла. Не совсем понятен фокус с выделением куска памяти *Size

Можно описать, чего хотелось и что получилось или не получилось?
Маньяк Робокряк колесит по городу
Re: Собственный аллокатор с задаваемым выделением памяти
От: watchmaker  
Дата: 26.03.18 20:56
Оценка: +3
Здравствуйте, avovana, Вы писали:


A>Прокачать аллокатор для использования с std::map(и в будущем для собственного list'a), с имитацией поведения метода reserve(), который есть у std::vector.


А знаешь почему у std::vector есть метод reserve, а у std::map его нет? Ведь про него не забыли — существует причина по которой он отсутствует


A>Вот что я пробовал, но у меня не получилось.


Так и не получится. std::map будет всегда запрашивать у аллокатора память для одного узла дерева за раз.

Но выделять память блоками, конечно можно. Просто для этого тебе нужно использовать stateful-аллокатор.
То есть твой пользовательский аллокатор, который передаётся в std::map, должен хранить указатель (или ссылку) на родительский аллокатор или, как его лучше назвать, memory pool.
И в методе allocate не выделять память вызовом malloc, а отдавать указатель на уже выделенную память в родительском пуле. А если её там не хватает, то пусть уже родительский пул аллоцирует очередной большой блок памяти.
Re[2]: Собственный аллокатор с задаваемым выделением памяти
От: avovana Россия  
Дата: 27.03.18 08:18
Оценка:
Спасибо за ответы!

M>Всё когда-то бывает в первый раз

Представляю, что разобраться будет не просто
Уже порядочно запутался, т.к. не могу найти простого мануала, а везде какие-то куски.

M>По идее, аллокатор для map вызывается для аллокации памяти для каждого узла. Не совсем понятен фокус с выделением куска памяти *Size


M>Можно описать, чего хотелось и что получилось или не получилось?


Сейчас есть такое задание — чтобы саллоцироватьп память заранее для последующего конструирования элементов.
Вот и хотелось как-нибудь(к примеру параметром) передать под сколько элементов нужно заранее саллоцировать память.
Как в std::vector::reserve(Size).

Теперь мне понимаю, что метод allocate у аллокатора std::map будет вызывать каждый раз.

M>А знаешь почему у std::vector есть метод reserve, а у std::map его нет? Ведь про него не забыли — существует причина по которой он отсутствует

No...

M>Так и не получится. std::map будет всегда запрашивать у аллокатора память для одного узла дерева за раз.

M>Но выделять память блоками, конечно можно. Просто для этого тебе нужно использовать stateful-аллокатор.
M>То есть твой пользовательский аллокатор, который передаётся в std::map, должен хранить указатель (или ссылку) на родительский аллокатор или, как его лучше назвать, memory pool.
M>И в методе allocate не выделять память вызовом malloc, а отдавать указатель на уже выделенную память в родительском пуле. А если её там не хватает, то пусть уже родительский пул аллоцирует очередной большой блок памяти.

Прочитал статью arena allocator.
Мне кажется, Вы говорите примерно об этом.
Сложно она у меня пошла. Такое чувство, что авторы, пишущие на данную тему уже подразумевают, что читатель знает это, это и это + основную логику работы аллокатора.
А для новичка(для меня) очень сложно.
Самое простое, что нашел из книги Джосатиса — myalloc.cpp.

1. Правильно ли я понимаю, что в аллокатор:
  template <typename T>
  struct Allocator {
  ...
  }

нужно будет добавить объект pool?

2. Правильно ли я понимаю, что:
стоит реализовать метод pointer allocate(size_type n) не так:
    pointer allocate(size_type n) {

      void *ptr = malloc(sizeof(value_type) * n);
      if (ptr == nullptr)
        throw std::bad_alloc();
      return static_cast<pointer>(ptr);
    }

А использовать внутри этот самый pool вместо malloc'a внутри которого и будет реализована логика выделения памяти и предоставления указателя void *ptr наверх(в allocate).
Т.е. фактически строчка:
void *ptr = malloc(sizeof(value_type) * n);
замениться на:
void *ptr = pool.getMemory(sizeof(value_type) * n);
?
Отредактировано 27.03.2018 8:22 avovana . Предыдущая версия . Еще …
Отредактировано 27.03.2018 8:20 avovana . Предыдущая версия .
Re[2]: Собственный аллокатор с задаваемым выделением памяти
От: avovana Россия  
Дата: 28.03.18 04:59
Оценка:
Ответил, но чуть выше по теме. Немного не разобрался в иерархии ответов.
Re[3]: Собственный аллокатор с задаваемым выделением памяти
От: watchmaker  
Дата: 29.03.18 14:44
Оценка:
Здравствуйте, avovana, Вы писали:

A>Прочитал статью arena allocator.


Да, это правильное направление. В этой статье как раз за операции по выделению памяти отвечают пара сущностей. Arena отвечает за выделение больших блоков (в статье их число, правда, ограничено ровно одним на одну арену), а аллокатор только содержит указатель на его арену и лишь нарезает её память на мелкие куски запрашиваемого объёма.

A>1. Правильно ли я понимаю, что в аллокатор:

A>нужно будет добавить объект pool?

Скорее не сам pool, а указатель на него. Ведь аллокаторы можно и нужно уметь копировать. А обычно в этом случае ведь не нужно копировать саму выделенную память, которую держит объект pool.

A>Т.е. фактически строчка:

A> void *ptr = malloc(sizeof(value_type) * n);
A>замениться на:
A> void *ptr = pool.getMemory(sizeof(value_type) * n);
A>?
Ну можно так, да.
А внутри функции pool::getMemory можно выделить больше памяти, чем запрошено. И информацию об этом сохранить в объекте pool, тогда при последующих вызовах getMemory можно не выделять новые блоки, а использовать остатки от уже выделенного.
Re[4]: Собственный аллокатор с задаваемым выделением памяти
От: avovana Россия  
Дата: 30.03.18 09:30
Оценка:
W>А внутри функции pool::getMemory можно выделить больше памяти, чем запрошено. И информацию об этом сохранить в объекте pool, тогда при последующих вызовах getMemory можно не выделять новые блоки, а использовать остатки от уже выделенного.

На данный момент получился такой вариант:
пример работающего кода.

Собственный контейнер работает как с созданным аллокатором, так и со стандартным.

Приведу код и здесь, если в будущем кто-нибудь заинтересуется, а ссылка станет недоступной:

Вызов:
// forward_list example + rebind + refactoring + standart allocator/custom allocator

#include <iostream>
#include <cstdlib>
#include <vector>
#include <map>

#include "allocatorarena.h"
#include "forward_list.h"

int main()
{
    ForwardList<int, ArenaAllocator<int>> list;
    //ForwardList<int> list;
    for(size_t i = 0; i < 10; ++i)
    {
        list.append(i);
    }
    
    list.display();    
}


Собственный контейнер:
#include<iostream>
using namespace std;

template <typename T>
struct Node
{
    Node(const T& _data)
        : data{_data}
        , next(nullptr)
        { }
        
    T data;
    Node<T> *next;    
};

template <typename T, typename MyAllocator = allocator<T>>
class ForwardList
{
    private:
        Node<T> *head = nullptr;
        Node<T> *tail = nullptr;
        
        using alloc_t = typename MyAllocator::template rebind<Node<T>>::other;
        alloc_t my_allocator;
        
    public:
        void append(T value)
        {
            auto newNode = my_allocator.allocate(1);       
            my_allocator.construct(newNode, value);           
            
            newNode->data = value;
            newNode->next = nullptr;
            
            if(head == nullptr)
            {
                head = newNode;
                tail = newNode;
                newNode = nullptr;
            }
            else
            {    
                tail->next = newNode;
                tail = newNode;
            }
        }
        
        void display()
        {
            auto currentNode = head;
            
            while(currentNode != nullptr)
            {
                cout << currentNode->data << "\t";
                currentNode = currentNode->next;
            }
        }
};


Собственный аллокатор:
#include <type_traits>
#include "arena.h"

template <typename T>
struct ArenaAllocator
{
    template <typename U> friend struct ArenaAllocator;

    using value_type = T;
    using pointer = T *;

    explicit ArenaAllocator(Arena * a) : arena(a) {}
    
    template <class U> 
    struct rebind {
        using other =  ArenaAllocator<U>; 
    };

    ArenaAllocator()
    {
        arena = new Arena(400);
    }

    ~ArenaAllocator()
    {
        delete arena;
    }

    template <typename U>
    ArenaAllocator(ArenaAllocator<U> const & rhs) : arena(rhs.arena) {}

    pointer allocate(std::size_t n)
    {
        return static_cast<pointer>(arena->allocate(n * sizeof(T), alignof(T)));
    }

    void deallocate(pointer p, std::size_t n)
    {
        arena->deallocate(p, n * sizeof(T));
    }

    template <typename ... Args >
    void construct(T* p, Args&& ... args) {
        new(p) T(std::forward <Args>(args) ... );
    };

    template <typename U>
    bool operator==(ArenaAllocator<U> const & rhs) const
    {
        return arena == rhs.arena;
    }

    template <typename U>
    bool operator!=(ArenaAllocator<U> const & rhs) const
    {
        return arena != rhs.arena;
    }

private:
    Arena * arena;
};


Помощник аллокатору:
#include <iostream>

class Arena
{
    unsigned char * const data;
    std::size_t const size;
    std::size_t offset;

public:
    explicit Arena(std::size_t s)
    : data(static_cast<unsigned char *>(::operator new(s)))
    , size(s)
    , offset(0)
    {
        std::cout << "arena[" << this << "] of size " << size << " created.\n";
    }

    Arena(Arena const &) = delete;
    Arena & operator=(Arena const &) = delete;

    ~Arena()
    {
        std::cout << "arena[" << this << "] destroyed; final fill level was: " << offset << "\n";
        ::operator delete(data);
    }

    void * allocate(std::size_t n, std::size_t a)
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        std::cout << " " << "size is:  " << n << std::endl;
        std::cout << " " << "align is: " << a << std::endl;
        offset = (offset + a - 1) / a * a;

        std::cout << "arena[" << this << "] allocating " << n << " bytes at offset " << offset << ".\n";

        if (offset + n > size)
        {
            throw std::bad_alloc();
        }

        void * result = data + offset;
        offset += n;
        return result;
    }

    void deallocate(void *, std::size_t n)
    {
        std::cout << "arena[" << this << "] may deallocate " << n << " bytes.\n";
    }
};


В следующей итерации собираюсь:
1) перенести функциональность помощника аллокатора(Arena) в класс аллокатора.
2) реализовать собственному аллокатору возможность задавать начальное кол-во выделяемой памяти путем передачи шаблонного параметра.
Re[5]: Собственный аллокатор с задаваемым выделением памяти
От: watchmaker  
Дата: 30.03.18 11:34
Оценка:
Здравствуйте, avovana, Вы писали:


A>пример работающего кода.

Ну назвать этот код работающим можно весьма условно :)

Например, выполнение такой безобидной конструкции приведёт к падению программы:
   std::list<int, ArenaAllocator<int>> list;
   auto list2 = list;





A>    ArenaAllocator()
A>    {
A>        arena = new Arena(400);
A>    }

Это плохо. В статье совершенно не зря передавали указатель на арену в конструктор извне, а не конструировали внутри.

A>    ~ArenaAllocator()
A>    {
A>        delete arena;
A>    }

Надо бы использовать идиому RAII. Ну и разобраться когда нужно удалять арену, а когда не нужно.
Re: Собственный аллокатор с задаваемым выделением памяти
От: Константин Черногория  
Дата: 01.04.18 13:52
Оценка:
Здравствуйте, avovana, Вы писали:

A>Прокачать аллокатор для использования с std::map(и в будущем для собственного list'a), с имитацией поведения метода reserve(), который есть у std::vector.


Это для производительности нужно?
Если да, возьми мой готовый:
https://github.com/Const-me/CollectionMicrobench
Тестировал немного, но на венде с msvc и линупсе с clang и libc++ оно у меня работало OK.
Только связные списки с ним сломаются, если делить списки надвое, или перемещать ноды между списками.
Re[2]: Собственный аллокатор с задаваемым выделением памяти
От: avovana Россия  
Дата: 07.04.18 16:39
Оценка:
Здравствуйте, Константин, Вы писали:

К>Это для производительности нужно?


Очень интересный пример, посмотрел. Мощный.
Но у меня свой маленький проект.
Сейчас прохожу курс С++ программиста.
После очередной лекции было такое дз по созданию собственного аллокатора и list'a.

Здравствуйте, watchmaker, Вы писали:
w>Ну назвать этот код работающим можно весьма условно

Решил уйти от Arena.

Ситуация на данный момент следующая.
Пока list отложил в сторону, занят аллокатором.
На данный момент сделано задание выделяемой памяти аллокатору с помощью параметра.
Раньше был голый указатель на выделяемую память и выделялось всё отлично:
template <typename T, size_t Size = 400>
struct Allocator {
    using value_type = T;
    using pointer = T *;

    unsigned char * const data;
    std::size_t const size;
    std::size_t offset;  

    Allocator()
    : data(static_cast<unsigned char *>(::operator new(Size)))
    , size(Size)
    , offset(0)
    { }

    pointer allocate(std::size_t n)
    {
        if (offset + n > size)
        {
            throw std::bad_alloc();
        }

        void * result = data + offset;
        offset += n;
        
        return static_cast<pointer>(result);
    }
...
}


Но освобождение памяти, я так понимаю, не происходило. Надо было обернуть data в unique_ptr, что было сделано(про что писали — RAII).
Но полетели ошибки.

Вот код:
https://wandbox.org/permlink/2ESCMj6eA6tT477R

В основном файле создается std::map с собственным аллокатором. std::map заполняется элементами.
В файле allocatorarena.h реализация аллокатора. Функционал arena'ы уже выброшен. Есть просто аллокатор.
Вспомогательные файлы newdelete.h и newdelete.cc помогают сделать видимыми выделение и освобождение памяти.
В файле forward_list.h реализация контейнера, что пока не нужно.

Сам немного запутался и не могу понять что не так делается.

Need help
Отредактировано 07.04.2018 18:35 avovana . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.