4

Я работаю над реализацией распределения памяти/памяти, и я настраиваю его в поместье, где только специальный тип объекта «Клиент» может рисовать из pool. Клиент может быть сконструирован непосредственно в пуле, или он может использовать пул для вызовов динамической памяти, или теоретически можно сделать оба. Я хотел был бы иметь возможность перегрузить оператор new и оператор удаляет таким образом, который будет называть мои пулы функциями «alloc()» и «free()», чтобы получить память, необходимую для построения объекта на.Использование оператора new и оператора delete с использованием собственного пула памяти/распределителя

Одна из основных проблем, которые возникают у меня, - это удалить мой оператор, чтобы освободить память, вызвав функцию pool-> free(), которую я написал. Я придумал хак, который исправляет это, передавая пул в конструктор, и деструктор выполняет работу по освобождению. Все это прекрасно и денди, пока кому-то не нужно наследовать от этого класса и переопределить деструктор для собственных нужд, а затем забывает делать освобождение памяти. Вот почему я хочу обернуть все это в операторы, чтобы функциональность была убрана и унаследована по умолчанию.

Мой код на GitHub здесь: https://github.com/zyvitski/Pool

Мое определение класса для клиента выглядит следующим образом:

class Client 
{ 
public: 
    Client(); 
    Client(Pool* pool); 
    ~Client(); 

    void* operator new(size_t size,Pool* pool); 
    void operator delete(void* memory); 

    Pool* m_pPool; 
}; 

И реализация:

Client::Client() 
{ 

} 
Client::Client(Pool* pool) 
{ 
    m_pPool = pool; 
} 
Client::~Client() 
{ 
    void* p = (void*)this; 
    m_pPool->Free(&p); 
    m_pPool=nullptr; 
} 
void* Client::operator new(size_t size, Pool* pool) 
{ 
    if (pool!=nullptr) { 
     //use pool allocator 
     MemoryBlock** memory=nullptr; 
     memory = pool->Alloc(size); 
     return *memory; 
    } 
    else throw new std::bad_alloc; 
} 
void Client::operator delete(void* memory) 
{ 
    //should somehow free up the memory back to the pool 
    // the proper call will be: 
    //pool->free(memory); 
    //where memory is the address that the pool returned in operator new 

} 

Вот пример Main(), которые я использую на данный момент:

int main(int argc, const char * argv[]){ 
    Pool* pool = new Pool(); 
    Client* c = new(pool) Client(pool); 
    /* 
    I'm using a parameter within operator new to pass the pool in for use and i'm also passing the pool as a constructor parameter so i can free up the memory in the destructor 
    */ 

    delete c; 
    delete pool; 
    return 0; 
} 

До сих пор мой код работает, но я хочу знать, есть ли лучший способ достичь этого? Пожалуйста, дайте мне знать, если что-либо, о чем я прошу/делаю, просто невозможно, плохая практика или просто просто немой. Я сейчас на MacBook Pro, но я хотел бы сохранить свою кросс-платформу кода, если это вообще возможно.

Если у вас есть вопросы, которые помогут вам помочь мне, дайте мне знать.

И, конечно же, заранее заблаговременно всем, кто может помочь.

ответ

4

Вы можете хранить дополнительную информацию как раз перед возвращенным адресом памяти

#include <iostream> 
#include <type_traits> 

class Pool { 
public: 
    static void* Alloc(std::size_t size) { return data; } 
    static void Dealloc(void*) {} 
private: 
    static char data[1024]; 
}; 
char Pool::data[1024]; 


class Client 
{ 
public: 
    void* operator new(size_t size, Pool& pool); 
    void operator delete(void* memory); 
}; 


struct MemoryHeader { 
    Pool* pool; 
}; 


void* Client::operator new(size_t size, Pool& pool) 
{ 
    auto header = static_cast<MemoryHeader*>(pool.Alloc(sizeof(MemoryHeader) + size)); 
    std::cout << " New Header: " << header << '\n'; 
    header->pool = &pool; 
    return header + 1; 
} 

void Client::operator delete(void* memory) 
{ 
    auto header = static_cast<MemoryHeader*>(memory) - 1; 
    std::cout << " Delete Header: " << header << '\n'; 
    header->pool->Dealloc(header); 
} 

int main() 
{ 
    Pool pool; 
    Client* p = new(pool) Client; 
    std::cout << "Client Pointer: " << p << '\n'; 
    delete p; 
    return 0; 
} 
+0

До сих пор мне нравится этот ответ. Я вижу, что вы отредактировали его примерно через час после того, как вы разместили его? Мне любопытно, что и почему вы изменили. Я помню, что есть некоторые элементы выравнивания байтов, и вы использовали союз для чего-то. Если вы не возражаете объяснить, что мне интересно, что вы там делали и почему? –

+1

@AlexZywicki, что выровненное хранилище было бесполезным (A C++ 11 вещь не требуется здесь, я тоже изучаю) –

0

Если ваш оператор delete просто вызывает free, вы пользовательский Распределитель не делать E очень хорошую работой. Идея настраиваемого распределителя состоит в том, что он будет работать с предопределенной областью памяти, в которой он будет иметь контроль: когда он выделяет память, он будет из области или пула памяти, а когда освобождается память, распределитель «информирует» об этом может повторно использовать эту память. Теперь, если вы используете бесплатный, вы просто возвращаете память в кучу, а не в пул памяти. Обычно эта часть обычно делается с использованием интеллектуальных указателей - для отслеживания того, какая память доступна.

Любой другой механизм будет работать до тех пор, пока вы сможете отслеживать, какие адреса используются и которые доступны.

Надеется, что это помогает

+0

Спасибо. Мой пул-> free() не вызывает регулярную функцию free(), она делает то, что вы сказали. Вы можете взглянуть, если вы потянете репозиторий git, на который я разместил ссылку. Я не хотел ставить код для этого в вопросе, потому что он довольно громоздкий, а не то, о чем мой вопрос касается напрямую. –

1

С помощью Dieter Lücking я был в состоянии понять, как использовать свой бассейн в операторе нового и оператор удаление

Вот код для оператора нового :

void* ObjectBase::operator new(size_t size, Pool* pool) 
{ 
    if (pool!=nullptr) { 
     //use pool allocation 
     MemoryBlock** block = pool->Alloc(size+(sizeof(MemoryHeader))); 
     MemoryBlock* t = * block; 
     t = (MemoryBlock*)((unsigned char*)t+sizeof(MemoryHeader)); 
     MemoryHeader* header = new(*block)MemoryHeader(pool); 
     header=nullptr; 
     return t; 
    } 
    else{ 
     //use std allocation 
     void* temp = ::operator new(size); 
     if (temp!=nullptr) { 
      return temp; 
     } 
     else throw new std::bad_alloc; 
    } 
} 

Вот код для OPERAT или удалить

void ObjectBase::operator delete(void* memory) 
{ 
    MemoryBlock* temp = (MemoryBlock*)((unsigned char*)memory-sizeof(MemoryHeader)); 
    MemoryHeader* header = static_cast<MemoryHeader*>(temp); 
    if (header->pool!=nullptr) { 
     if (header->pool->Free((MemoryBlock**)&header)); 
     else 
     { 
      ::operator delete(memory); 
     } 
    } 
    else{ 
     ::operator delete(memory); 
    } 
} 

Я использую идею «заголовка памяти», которая была предложена.

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

Еще раз спасибо за помощь.

Смежные вопросы