假设我有一个结构MyStruct
,我想分配一个“大”内存块,如下所示:
std::size_t memory_chunk_1_size = 10;
MyStruct * memory_chunk_1 = reinterpret_cast <MyStruct *> (new char[memory_chunk_1_size * sizeof(MyStruct)]);
并且由于“任意原因”,我想将该内存块“拆分”为两个较小的块,而没有;移动数据,调整“动态数组”的大小,分配/分配/重新分配内存等。
所以我正在这样做:
std::size_t memory_chunk_2_size = 5; // to remember how many elements there are in this chunk;
MyStruct * memory_chunk_2 = &memory_chunk_1[5]; // points to the 6th element of memory_chunk_1;
memory_chunk_1_size = 5; // to remember how many elements there are in this chunk;
memory_chunk_1 = memory_chunk_1; // nothing changes still points to the 1st element.
不幸的是,当我尝试释放内存时,我遇到了一个错误:
// release memory from the 2nd chunk
for (int i = 0; i < memory_chunk_2_size ; i++)
{
memory_chunk_2[i].~MyStruct();
}
delete[] reinterpret_cast <char *> (memory_chunk_2); // deallocates memory from both "memory_chunk_2" and "memory_chunk_1"
// release memory from the 1st chunk
for (int i = 0; i < memory_chunk_1_size ; i++)
{
memory_chunk_1[i].~MyStruct(); // Throws exception.
}
delete[] reinterpret_cast <char *> (memory_chunk_1); // Throws exception. This part of the memory was already dealocated.
如何仅删除选定数量的元素(以解决此错误)?
可编译的示例:
#include <iostream>
using namespace std;
struct MyStruct
{
int first;
int * second;
void print()
{
cout << "- first: " << first << endl;
cout << "- second: " << *second << endl;
cout << endl;
}
MyStruct() :
first(-1), second(new int(-1))
{
cout << "constructor #1" << endl;
print();
}
MyStruct(int ini_first, int ini_second) :
first(ini_first), second(new int(ini_second))
{
cout << "constructor #2" << endl;
print();
}
~MyStruct()
{
cout << "destructor" << endl;
print();
delete second;
}
};
int main()
{
// memory chunk #1:
std::size_t memory_chunk_1_size = 10;
MyStruct * memory_chunk_1 = reinterpret_cast <MyStruct *> (new char[memory_chunk_1_size * sizeof(MyStruct)]);
// initialize:
for (int i = 0; i < memory_chunk_1_size; i++)
{
new (&memory_chunk_1[i]) MyStruct(i,i);
}
// ...
// Somewhere here I decided I want to have two smaller chunks of memory instead of one big,
// but i don't want to move data nor reallocate the memory:
std::size_t memory_chunk_2_size = 5; // to remember how many elements there are in this chunk;
MyStruct * memory_chunk_2 = &memory_chunk_1[5]; // points to the 6th element of memory_chunk_1;
memory_chunk_1_size = 5; // to remember how many elements there are in this chunk;
memory_chunk_1 = memory_chunk_1; // nothing changes still points to the 1st element.
// ...
// some time later i want to free memory:
// release memory from the 2nd chunk
for (int i = 0; i < memory_chunk_2_size ; i++)
{
memory_chunk_2[i].~MyStruct();
}
delete[] reinterpret_cast <char *> (memory_chunk_2); // deallocates memory from both "memory_chunk_2" and "memory_chunk_1"
// release memory from the 1st chunk
for (int i = 0; i < memory_chunk_1_size ; i++)
{
memory_chunk_1[i].~MyStruct(); // Throws exception.
}
delete[] reinterpret_cast <char *> (memory_chunk_1); // Throws exception. This part of the memory was already dealocated.
// exit:
return 0;
}
答案 0 :(得分:3)
C ++编程语言不支持这种选择性释放,并且可能永远不会支持。
如果您打算取消分配内存的各个部分,则首先需要对这些各个部分进行单独分配。
可能某个特定的OS或平台可能支持这种行为,但这将是特定于OS的系统调用,而不是通过C ++标准语言语法。
答案 1 :(得分:1)
使用malloc或new分配的内存不能部分取消分配。许多堆使用不同大小分配的垃圾箱来提高性能并防止碎片,因此允许部分释放将使这种策略成为不可能。
这当然不会阻止您编写自己的分配器。
答案 2 :(得分:1)
说实话:这是非常糟糕的做法!尝试强制转换new
和delete
并自称为两者之间的析构函数是低级手动内存管理的证据。
替代
在C ++中管理连续块中动态内存结构的正确方法是使用std::vector
而不是手动数组或手动内存管理,然后继续执行该库。您可以resize()
向量来删除不需要的元素。您可以shrink_to_fit()
说,您不再需要额外的可用容量,尽管不能保证释放了不需要的内存。
应避免使用C内存分配函数,尤其是realloc()
,因为它很容易出错,并且仅适用于trivially copiable objects。
修改:您自己的容器
如果您要实现自己的特殊容器,并且由于特殊的特殊约束而必须允许这种动态行为,那么您应该考虑编写自己的内存管理功能,该功能可以管理一种“私有堆”。
堆管理通常通过linked list of free chunks来实现。
一种策略是在私有堆中没有足够的连续内存时分配新的块。然后,您可以提供更为宽松的myfree()
函数,该函数将已释放或部分释放的块重新插入到该链接列表中。当然,这需要遍历链接列表,以查找释放的内存是否与私有堆中的任何其他空闲内存块相邻,并在相邻时合并这些块。
我看到MyStruct
很小。然后,另一种方法是编写一个特殊的分配函数,该函数针对小型固定大小的块进行了优化。 Loki's small object library中有一个示例,在“ Modern C++ Design”中有深入介绍。
最后,您也许可以看看Pool library of Boost,它也提供了chunk based approach。
答案 3 :(得分:1)
我可以通过标准c ++想到的最简单的方法是遵循以下惯用代码:
std::vector<int> v1(1000);
auto block_start = v1.begin() + 400;
auto block_end = v1.begin() + 500;
std::vector<int> v2(block_start,block_end);
v1.erase(block_start,block_end);
v1.shrink_to_fit();
如果编译器足够智能,可以将这种模式转换为最有效的低级OS和CPU内存管理操作,则是实现细节。
这里是working example。