Estoy haciendo mi propio asignador dinámico en C ++. Pero me encontré con un problema en el que no puedo liberar mi memoria. Este es el código para Test.cpp:

#include "Memory/MemoryManager.h"

int main(){
    initMemory(1);
    int* p = allocate<int>();
    int* q = allocate<int>();
    int* r = allocate<int>();
    cout<<p<<endl;
    cout<<q<<endl;
    cout<<r<<endl;
    freeAddress<int>(q);
    return 0;
}

MemoryManager.h:

#ifndef MEMORY_MANAGER_INCLUDED
#define MEMORY_MANAGER_INCLUDED

#include <iostream>
#include <map>
#include <memory>

using namespace std;

char* memory;
char* current;

map<void*, size_t> freePointers;

void initMemory(size_t size){
    memory = (char*)malloc(size);
    current = memory;
}

template<typename T> T* allocate(){
    T* address = NULL;
    for (auto p : freePointers){
        if (p.second == sizeof(T)){
            address = static_cast<T*>(p.first);
        }
    }

    if (address == NULL){
        address = new(current) T();
        current += sizeof(T);
    }

    return address;
}

template<typename T> T* allocate(size_t size){
    T* address = NULL;
    for (auto p : freePointers){
        if (p.second == sizeof(T) * size){
            return static_cast<T*>(p.first);
        }
    }

    if (address == NULL){
        address = new(current) T[size];
        current += sizeof(T) * size;
    }
    return address;
}

template<typename T> void freeAddress(T* address){
    freePointers.insert({(void*)address, sizeof(*address)});
    delete address;
}

template<typename T> void freeAddress(T* address, size_t size){
    freePointers.insert({(void*)address, sizeof(*address) * size});
    delete [] address;
}
#endif

Salida:

0x55ee37729e70
0x55ee37729e74
0x55ee37729e78
0x55ee37729e70
0x55ee37729e78
free(): invalid pointer

Sé que no puedo eliminar punteros en la memoria de la pila, pero no lo estoy usando en absoluto. Además, indique si estoy haciendo algo mal o si el rendimiento es muy costoso. Por favor ayuda.

0
SarojKr 5 dic. 2019 a las 10:26

2 respuestas

La mejor respuesta

Usted asigna una gran cantidad de memoria con malloc, luego llama a delete en algún puntero en la memoria asignada.

En su código, q es (memory + sizeof(int)). Este puntero nunca ha sido devuelto por una asignación y, por lo tanto, no se puede liberar. Esta es la razón del error.

Además, la falta de coincidencia malloc con delete es un comportamiento indefinido. En su lugar, debe llamar free() en el puntero original memory como limpieza al final, y nunca llamar a delete en valores individuales.

Si desea llamar al destructor en freeAddress, use address->~T();.

3
flyx 5 dic. 2019 a las 07:45

Está asignando un solo bloque de memoria de 1 byte una vez, en su initMemory(). Está utilizando malloc() para esa asignación, de modo que el bloque de memoria debe liberarse con free() cuando haya terminado de usarlo, pero no lo está haciendo.

Su freeAddress() es delete 'memoria que nunca fue asignada con new. Dentro de allocate(), está utilizando ubicación-nueva en lugar de new, no son lo mismo. Cuando utiliza ubicación-nueva , debe llamar al destructor de un objeto manualmente, no free() o delete.

Y ciertamente no desea liberar memoria que va a reutilizar más tarde. Eso derrota todo el propósito de almacenar en caché la memoria "liberada".

Y new[] usa más memoria de la que usted solicita, de modo que puede almacenar información para delete[] para saber cuántos elementos liberar y cómo liberarlos. No sabrá cuánto gasto adicional es, ya que se define la implementación. Por lo tanto, no es seguro usar new[] en esta situación.

Prueba algo más como esto

#include "Memory/MemoryManager.h"

int main(){
    initMemory(sizeof(int) * 3);
    int* p = allocate<int>();
    int* q = allocate<int>();
    int* r = allocate<int>();
    cout << p << endl;
    cout << q << endl;
    cout << r << endl;
    freeAddress<int>(q);
    doneMemory();
    return 0;
}
#ifndef MEMORY_MANAGER_INCLUDED
#define MEMORY_MANAGER_INCLUDED

#include <iostream>
#include <map>
#include <memory>

char* memory;
char* current;
size_t available;
std::map<void*, size_t> freePointers;

void initMemory(size_t size){
    memory = (char*) malloc(size);
    current = memory;
    available = (memory) ? size : 0;
}

void doneMemory(){
    freePointers.clear();
    free(memory);
    memory = current = nullptr;
    available = 0;
}

template<typename T>
T* allocate(){
    T *address = nullptr;

    for (auto iter = freePointers.begin(); iter != freePointers.end(); ++iter){
        if (iter->second == sizeof(T)){
            address = static_cast<T*>(iter->first);
            freePointers.erase(iter);
            break;
        }
    }

    if (!address){
        if (available < sizeof(T)){
            return nullptr;
        }
        address = static_cast<T*>(current);
        current += sizeof(T);
        available -= sizeof(T);
    }

    return new(address) T();
}

template<typename T>
T* allocate(size_t count){
    T *address = nullptr;
    size_t size = count * sizeof(T);

    for (auto iter = freePointers.begin(); iter != freePointers.end(); ++iter){
        if (iter->second == size){
            address = static_cast<T*>(iter->first);
            freePointers.erase(iter);
            break;
        }
    }

    if (!address){
        if (available < size){
            return nullptr;
        }
        address = static_cast<T*>(current);
        current += size;
        available -= size;
    }

    for(size_t i = 0; i < count; ++i)
        new(address+i) T();
    }

    return address;
}

template<typename T>
void freeAddress(T* address){
    address->~T();
    freePointers.insert({(void*)address, sizeof(T)});
}

template<typename T>
void freeAddress(T* address, size_t count){
    for (size_t i = 0; i < count; ++i)
        address[i].~T();
    freePointers.insert({(void*)address, sizeof(T) * count});
}

#endif

Dicho esto, este no es un asignador de memoria muy seguro o robusto, pero debería ayudarlo a comenzar.

Si realmente desea crear un asignador de memoria personalizado, debe escribir una clase que siga el { {X0}}, y luego puedes usar ese asignador con contenedores C ++ estándar, como std::vector. Deje que el compilador y la biblioteca estándar manejen la mayor parte del trabajo duro por usted.

0
Remy Lebeau 5 dic. 2019 a las 08:45