抛弃std::allocator以支持自定义解决方案的一些真正好的理由是什么?您是否遇到过这样的情况:它对于正确性、性能、可伸缩性等来说是绝对必要的?有什么聪明的例子吗?

自定义分配器一直是标准库的一个特性,但我并不太需要它。我只是想知道是否有人能提供一些令人信服的例子来证明他们的存在。


当前回答

正如我在这里提到的,我已经看到英特尔TBB的自定义STL分配器仅通过更改单个STL分配器就显著提高了多线程应用程序的性能

std::vector<T>

to

std::vector<T,tbb::scalable_allocator<T> >

(这是一种快速方便的方式切换分配器使用TBB的漂亮的线程私有堆;见本文件第7页)

其他回答

对于共享内存来说,不仅容器头存储在共享内存中,而且容器头包含的数据也存储在共享内存中,这一点至关重要。

Boost::Interprocess的分配器就是一个很好的例子。然而,正如你在这里读到的,这个allone是不够的,要使所有STL容器共享内存兼容(由于不同进程中的映射偏移量不同,指针可能会“中断”)。

我正在研究一个mmap-分配器,它允许向量使用内存 内存映射文件。我们的目标是让向量使用这样的存储 直接在由mmap映射的虚拟内存中。我们的问题是 提高真正大的文件(>10GB)的读取到内存,而不复制 开销,因此我需要这个自定义分配器。

到目前为止,我已经有了一个自定义分配器的骨架 (它来源于std::allocator),我认为这是一个很好的开始 指向写自己的分配器。请随意使用这段代码 以任何你想要的方式:

#include <memory>
#include <stdio.h>

namespace mmap_allocator_namespace
{
        // See StackOverflow replies to this answer for important commentary about inheriting from std::allocator before replicating this code.
        template <typename T>
        class mmap_allocator: public std::allocator<T>
        {
public:
                typedef size_t size_type;
                typedef T* pointer;
                typedef const T* const_pointer;

                template<typename _Tp1>
                struct rebind
                {
                        typedef mmap_allocator<_Tp1> other;
                };

                pointer allocate(size_type n, const void *hint=0)
                {
                        fprintf(stderr, "Alloc %d bytes.\n", n*sizeof(T));
                        return std::allocator<T>::allocate(n, hint);
                }

                void deallocate(pointer p, size_type n)
                {
                        fprintf(stderr, "Dealloc %d bytes (%p).\n", n*sizeof(T), p);
                        return std::allocator<T>::deallocate(p, n);
                }

                mmap_allocator() throw(): std::allocator<T>() { fprintf(stderr, "Hello allocator!\n"); }
                mmap_allocator(const mmap_allocator &a) throw(): std::allocator<T>(a) { }
                template <class U>                    
                mmap_allocator(const mmap_allocator<U> &a) throw(): std::allocator<T>(a) { }
                ~mmap_allocator() throw() { }
        };
}

为了使用它,像下面这样声明一个STL容器:

using namespace std;
using namespace mmap_allocator_namespace;

vector<int, mmap_allocator<int> > int_vec(1024, 0, mmap_allocator<int>());

例如,每当分配内存时,就可以使用它记录日志。什么是必要的 是重新绑定结构,否则向量容器使用超类分配/释放 方法。

更新:内存映射分配器现在可以在https://github.com/johannesthoma/mmap_allocator上获得,并且是LGPL。您可以在项目中使用它。

使用自定义分配器来使用内存池而不是堆可能会很有用。这只是众多例子中的一个。

对于大多数情况,这肯定是一个不成熟的优化。但它在某些情况下(嵌入式设备、游戏等)非常有用。

一种基本情况:当编写必须跨模块(EXE/DLL)边界工作的代码时,必须保持分配和删除只发生在一个模块中。

我在Windows上的插件架构中遇到了这种情况。例如,如果你跨DLL边界传递一个std::string,任何字符串的重新分配都发生在它起源的堆中,而不是在DLL中的堆中,这可能是不同的*。

*实际上比这更复杂,如果你动态链接到CRT,这可能会工作。但是,如果每个DLL都有一个到CRT的静态链接,那么您将陷入痛苦的世界,在那里幻影分配错误不断发生。

我没有使用自定义STL分配器编写c++代码,但我可以想象一个用c++编写的web服务器,它使用自定义分配器自动删除响应HTTP请求所需的临时数据。自定义分配器可以在生成响应后立即释放所有临时数据。

自定义分配器(我已经使用过)的另一个可能的用例是编写一个单元测试来证明函数的行为不依赖于它的某些输入。自定义分配器可以用任何模式填充内存区域。