无法删除堆内存上的指针
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))
。该指针从未由分配返回,因此无法释放。这就是错误的原因。
此外,malloc
与 delete
不匹配是未定义的行为。您应该改为在原始 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
。让编译器和标准库为您处理大部分繁重的工作。
我正在用 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))
。该指针从未由分配返回,因此无法释放。这就是错误的原因。
此外,malloc
与 delete
不匹配是未定义的行为。您应该改为在原始 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
。让编译器和标准库为您处理大部分繁重的工作。