如何取消分配连续内存块的一部分?

时间:2018-07-11 17:15:26

标签: c++ memory-management


假设我有一个结构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;
}

4 个答案:

答案 0 :(得分:3)

C ++编程语言不支持这种选择性释放,并且可能永远不会支持。

如果您打算取消分配内存的各个部分,则首先需要对这些各个部分进行单独分配。

可能某个特定的OS或平台可能支持这种行为,但这将是特定于OS的系统调用,而不是通过C ++标准语言语法。

答案 1 :(得分:1)

使用malloc或new分配的内存不能部分取消分配。许多堆使用不同大小分配的垃圾箱来提高性能并防止碎片,因此允许部分释放将使这种策略成为不可能。

这当然不会阻止您编写自己的分配器。

答案 2 :(得分:1)

说实话:这是非常糟糕的做法!尝试强制转换newdelete并自称为两者之间的析构函数是低级手动内存管理的证据。

替代

在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