【问题标题】:Improvements for this C++ stack allocator?这个 C++ 堆栈分配器的改进?
【发布时间】:2010-10-20 18:20:44
【问题描述】:

对我的基于堆栈的分配器有什么建议吗? (除了建议使用具有私有/公共成员的类)

struct Heap
{
    void* heap_start;
    void* heap_end;
    size_t max_end;

    Heap(size_t size)
    {
        heap_start = malloc(size);
        heap_end = heap_start;
        max_end = size + (size_t) heap_start;
    }

    ~Heap()
    {
        ::free(heap_start);
    }

    void* allocate(size_t bytes)
    {

        size_t new_end = ((size_t) heap_end) + bytes;

        if( new_end > max_end )
            throw std::bad_alloc();

        void* output = heap_end;
        heap_end = (void*) new_end;
        return output;
    }

}

【问题讨论】:

  • 我的 C++ 内存池怎么样?
  • 只是想知道是否有任何方法可以优化它,或者更好的约定等。
  • 好的,修改标题以使其更清晰。
  • 这是一个线性分配器,如果您不关心解除分配或可以一次解除分配,这是完美的选择。我不会将其描述为堆。
  • @Tom 根据在线定义cplus.about.com/od/glossar1/g/heap.htm 堆是用于分配动态变量的任何内存块。

标签: c++ memory memory-management


【解决方案1】:

两个明显的问题:

1/ 你没有deallocate()

2/ A deallocate() 将很难用您当前的策略编写,除非您总是以与分配完全相反的顺序解除分配。您需要满足客户想要在您使用的部分中间释放内存的情况。

当然,如果你以相反的顺序释放,(2) 不是问题。如果你从不释放内存,(1) 也不是问题。

这取决于你想要它做什么。

【讨论】:

  • 和通常的内存池一样,您只需分配一堆对象,然后在 1 个函数调用中将它们全部释放。但是后来我完全忘记了尖牙的观点,如果它们不是 POD,我可能需要调用它们的每个析构函数。
  • 你只需要调用析构函数,如果它们有文件句柄/其他打开的对象不在池中。如果一切都只分配内存并且所有内存都在池中,那么您不需要调用析构函数。
【解决方案2】:

您的堆不允许释放。您将如何将它用于在 C++ 中使用 new 分配的对象?

【讨论】:

  • 你提出了一个很好的观点,我想我只是想包含 POD。
  • 即使使用 POD,您也必须处理操作员删除。如果您不这样做,将使用默认操作符 delete,这可能会使您的程序崩溃。
  • 你有什么推荐的?我需要覆盖全局删除运算符吗?
  • 如果您只将堆用于一组有限的类,您最好为它们覆盖操作符删除。
  • 通过这种分配器,您可以轻松地使用placement new。
【解决方案3】:
size_t new_end = ((size_t) heap_end) + bytes;

不好,永远不要做那样的事情,你假设 sizeof(size_t)==sizeof(void*),如果 bytes==(size_t)(-1) 这不起作用会发生什么

此外,您需要确保返回的指针是对齐的。 否则你会有问题。因此,根据您的平台,您需要确保字节数是 4 或 8 的倍数。

class {...
char *max_end,*head_end,*heap_start;
};

...
max_end=heap_start+size;
...
bytes=align_to_platform_specific_value(bytes);
if(max_end-heap_end >= bytes) {
   void* output = (void*)heap_end;
   heap_end+=bytes;
   return output;
}
throw std::bad_alloc();

建议?不要重新发明轮子。有很多很好的池库。

【讨论】:

  • 但是我没有使用char的原因是因为char不保证是1字节。据我所知, size_t 总是 void* 因为一个类型可以跨越整个地址空间。我的 gcc 编译器也不允许我做 void* 算术。但是你对对齐有一点看法:它是空间/时间的权衡。
  • 标准定义了 sizeof(char)=1,标准没有定义 sizeof(size_t)==sizeof(void*) 即使它在实践中很常见,但它确实定义了 sizeof(intptr_t)== sizeof(void*) (但 intptr_t 不适用于 VC++ 等一些编译器)。
  • “这是一个空间/时间的权衡。”这是正确性问题。在某些平台(如 ARM)上,如果您进行非对齐访问,进程可能会失败。此外,您的数据使用原子操作(例如互斥锁),如果它不是未对齐的,它将失败。
  • 哪里有说char保证为1字节的规范?我相信这是可能的,但我只是想亲自看看,以防万一有一个奇怪的编译器碰巧使用不同大小的字符。或者有人可能将 char 定义为宽字符。
  • @Artyom 我知道你知道 :) 但 Unknown 的第一条评论指出“char 不能保证为 1 字节”,这是不正确的。所以我想支持你的评论而不是反对它!
【解决方案4】:

您已经实现了一个基于堆栈的分配器。你不能不留空隙就解放出来。通常,池是指一块具有固定大小插槽的连续内存块,它们是双重链接的,以允许恒定时间添加和删除。

Here's one 可以作为指导。它与您的思路相同,但包括分配节点上的基本迭代器,并使用模板来识别类型。

【讨论】:

  • 啊,是的,这似乎是正确的术语。我还以为叫内存池呢。
猜你喜欢
  • 1970-01-01
  • 2014-10-04
  • 1970-01-01
  • 1970-01-01
  • 2012-07-29
  • 2018-06-29
  • 2011-06-11
  • 2011-12-01
  • 2017-12-23
相关资源
最近更新 更多