无法删除堆内存上的指针

Unable to delete pointer on heap memory

我正在用 C++ 制作自己的动态分配器。但是我 运行 遇到了无法释放内存的问题。 这是 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

输出:

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

我知道我不能删除堆栈内存上的指针,但我根本没有使用它。也请指出我是否做错了什么或性能开销大。请帮忙。

您使用 malloc 分配了一大块内存,然后在某个指针上调用 delete 进入分配的内存。

在您的代码中,q(memory + sizeof(int))。该指针从未由分配返回,因此无法释放。这就是错误的原因。

此外,mallocdelete 不匹配是未定义的行为。您应该改为在原始 memory 指针上调用 free() 作为最后的清理,并且永远不要在单个值上调用 delete

如果要在freeAddress中调用析构函数,请使用address->~T();

您在 initMemory() 中一次性分配了一个 1 字节的内存块。您正在使用 malloc() 进行该分配,因此当您使用完该内存块时,必须使用 free() 释放该内存块,但您没有这样做。

您的 freeAddress()delete 从未分配给 new 的内存。在 allocate() 中,您使用的是 placement-new 而不是 new,它们不是一回事。当你使用 placement-new 时,你必须手动调用对象的析构函数,而不是 free()delete 它。

而且您肯定不希望释放稍后要重用的内存。这违背了缓存 "freed" 内存的全部目的。

并且 new[] 使用的内存比您请求的多,因此它可以为 delete[] 存储信息以了解要释放多少元素以及如何释放它们。您不会知道有多少额外开销,因为它是实现定义的。所以在这种情况下使用new[]是不安全的。

试试更像这样的东西

#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

也就是说,这不是一个非常安全或可靠的内存分配器,但它应该可以帮助您入门。

如果你真的想创建一个自定义内存分配器,你应该写一个遵循 Allocator 策略的 class,然后你可以使用标准 C++ 容器的分配器,比如 std::vector。让编译器和标准库为您处理大部分繁重的工作。