c++ - 我应该将分配器作为函数参数传递吗? (我对分配器的误解)

标签 c++ c++11 memory memory-management allocation

我正在学习之后allocator通过阅读一些文章几天
( cppreferenceAre we out of memory ),
我对如何控制数据结构以某种方式分配内存感到困惑。

我很确定我误解了一些东西,
所以我会把剩下的问题分成很多 零件 使我的错误更容易被提及。

这是我(错误)理解的:-

片段

假设 B::generateCs()是一个生成 C 列表的函数来自 CPrototype 的列表.B::generateCs()用于 B()构造函数:-

class C          {/*some trivial code*/};
class CPrototype {/*some trivial code*/};
class B {
    public: 
    std::vector<C> generateCs() {  
        std::vector<CPrototype> prototypes = getPrototypes();
        std::vector<C> result;                     //#X
        for(std::size_t n=0; n < prototypes.size(); n++) {
            //construct real object  (CPrototype->C)
            result.push_back( makeItBorn(prototypes[n]) ); 
        }
        return result;
    }
    std::vector<C> bField;    //#Y
    B() {
        this->bField = generateCs();    //#Y  ; "generateCs()" is called only here
    }
    //.... other function, e.g. "makeItBorn()" and "getPrototypes()"
};

从上面的代码,std::vector<C>当前使用通用默认值 std::allocator .

为简单起见,从现在开始,假设只有 2 个分配器(在 std::allocator 旁边),
我可以自己编码或修改 somewhere
:-
  • 堆分配器
  • 堆栈分配器

  • 第 1 部分 (#X)

    可以使用特定类型分配器改进此代码段。
    它可以在 2 个位置进行改进。 ( #X#Y )
    std::vector<C>在线 #X似乎是一个堆栈变量,
    所以我应该使用 stack allocator :-
    std::vector<C,StackAllocator> result;   //#X
    

    这往往会产生性能增益。 ( #X 已完成。)

    第 2 部分 (#Y)

    接下来,更难的部分在B()构造函数。 ( #Y )
    如果变量 bField 就好了有适当的分配协议(protocol)。

    仅对调用者进行编码以显式使用分配器是无法实现的,
    因为构造函数的调用者只能做到最好:-
    std::allocator<B> bAllo;   
    B* b = bAllo.allocate(1);   
    

    bField的分配协议(protocol)没有任何影响.

    因此,选择正确的分配协议(protocol)是构造函数本身的职责。

    第 3 部分

    我不知道是否是 B 的实例将被构造为堆变量或堆栈变量。
    这很重要,因为此信息对于选择正确的分配器/协议(protocol)很重要。

    如果我知道它是哪一个(堆或堆栈),我可以更改 bField 的声明成为:-
    std::vector<C,StackAllocator> bField;     //.... or ....
    std::vector<C,HeapAllocator> bField;     
    

    不幸的是,由于信息有限(我不知道哪个是堆/堆栈,它可以是两个),
    这条路径(使用 std::vector )导致死胡同。

    第 4 部分

    因此,更好的方法是将分配器传递给构造函数:-
    MyVector<C> bField; //create my own "MyVector" that act almost like "std::vector"
    B(Allocator* allo) {
        this->bField.setAllocationProtocol(allo);  //<-- run-time flexibility 
        this->bField = generateCs();   
    }
    

    这很乏味,因为调用者必须将分配器作为附加参数传递,
    但没有其他方法。

    此外,当有很多调用者时,这是获得以下数据一致性优势的唯一实用方法,每个调用者都使用自己的内存块:-
    class System1 {
        Allocator* heapForSystem1;
        void test(){
            B b=B(heapForSystem1);
        }
    };
    class System2 {
        Allocator* heapForSystem2;
        void test(){
            B b=B(heapForSystem2);
        }
    };
    


  • 我从哪里开始出错了,怎么办?
  • 如何改进片段以使用适当的分配器( #X#Y )?
  • 我什么时候应该将分配器作为参数传递?

  • 很难找到使用分配器的实际示例。

    编辑 (回复沃尔特)

    ... using another than std:allocator<> is only rarely recommendable.



    对我来说,这是沃尔特回答的核心。
    如果它是可靠的,那将是一种宝贵的知识。

    1. 有没有书/链接/引用/证据支持它?
    该列表不支持该声明。 (它实际上支持相反的一点。)
    是不是来自个人经验?

    2. 答案在某种程度上与许多来源相矛盾。请辩护。
    有很多来源建议不要使用std:allocator<> .
  • Are we out of memory :
    无法回答“您为子系统 X 使用了多少内存?”是有罪的。
  • Custom C++ allocators suitable for video games
    这意味着自定义分配器是主机游戏的必需品。
    @ 部分“为什么要替换默认分配器?”
  • Memory Management part 1 of 3
    没有自定义分配器 =“时不时会有一点延迟(在游戏中)”

  • 更具体地说,它们只是一种在现实世界中很少值得使用的“炒作”吗?

    另一个小问题:-
    是否可以将声明扩展为“大多数质量的游戏很少使用自定义分配器”?

    3.如果我遇到这种罕见的情况,我必须支付费用,对吗?

    只有两种好方法:-
  • 将分配器作为模板参数传递,或
  • 作为函数(包括构造函数)的参数
  • (另一个不好的方法是创建一些关于使用什么协议(protocol)的全局标志)

  • 这是正确的吗?

    最佳答案

    在 C++ 中,用于标准容器的分配器与容器类型相关(但见下文)。因此,如果你想控制你的类(包括它的容器成员)的分配行为,分配器必须是类型的一部分,即你必须将它作为 template 传递。范围:

    template<template <typename T> Allocator>
    class B
    {
    public:
      using allocator = Allocator<C>
      using fieldcontainer = std::vector<C,allocator>;
      B(allocator alloc=allocator{})
      : bFields(create_fields(alloc)) {}
    private:
      const fieldcontainer bFields;
      static fieldcontainer create_fields(allocator);
    };
    

    但是请注意,有实验 polymorphic allocator support ,它允许您独立于类型更改分配器行为。这当然比设计您自己的更可取 MyVector<>模板。

    请注意,使用的不是 std::allocator<>只有在有充分理由的情况下才值得推荐。可能的情况如下。
  • 对于频繁分配和取消分配的小对象,堆栈分配器可能是首选,但即使是堆分配器也可能不会降低效率。
  • 一个分配器,提供对齐到 64 字节的内存(适用于对齐加载到 AVX 寄存器)。
  • cache-aligned allocator可用于避免多线程情况下的错误共享。
  • 分配器可以 avoid default initialising trivially constructible对象以提高多线程设置中的性能。


  • 为回答其他问题而添加的注释。

    文章Are we out of memory日期从 2008 年开始,不适用于当代 C++ 实践(使用 C++11 标准或更高版本),当使用 std 进行内存管理时容器和智能指针( std::unique_ptr std::shared_ptr )避免了内存泄漏,这是在编写不佳的代码中增加内存需求的主要来源。

    在为某些特定应用程序编写代码时,可能有充分的理由使用自定义分配器——而 C++ 标准库支持这一点,因此这是一种合法且适当的方法。好的理由包括上面已经列出的那些,特别是当在多线程环境中需要高性能或通过 SIMD 指令实现时。

    如果内存非常有限(如某些游戏机上可能如此),则自定义分配器无法真正神奇地增加内存量。所以在这种情况下,分配器的使用,而不是分配器本身,才是最关键的。不过,自定义分配器可能有助于减少内存碎片。

    关于c++ - 我应该将分配器作为函数参数传递吗? (我对分配器的误解),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41921316/

    相关文章:

    c - 使用/dev/kmem 从内核读取地址值

    c++ - 如何使用QColumnView显示多列数据

    C++ & ta_lib - 无法计算出如何使用 TA_MACD 函数

    覆盖内存范围的算法?

    c++ - 每个 lambda 函数都是匿名类吗?

    c++ - std::vector 初始化元素的 move/复制构造函数

    android -/proc/slabinfo 提供什么信息?

    c++ - 为什么我的图书馆管理程序没有写入文件

    c++ - fopen 非 ASCII 字符错误

    c++ - Windows on Linux 交叉编译