思路:

    针对引用计数对象:

    1. static MemoryPool<Sprite> _spritePool;
    2. static MemoryPool<Label> _LabelPool;
    3. // 可代替所有new Ref的地方,比如:
    4. // new(nothrow) Sprite
    5. // new(nothrow) Label
    6. // 创建时:从内存池获得内存块进行构造
    7. // referencecount=0要被delete时,析构对象然后回收内存。
    8. auto sprite = _spritePool.createRef(...); // 三个点是构造函数的参数,任意类型任意数量
    9. auto label = _LabelPool.createRef(...);
    10. // 创建多个,...参数可为空(执行默认构造),不为空,则每个对象都用这些参数构造。
    11. std::vector<Sprite*> sprites = _spritePool.createRefN(10,...);

    针对非引用计数对象

    1. class MotherFucker
    2. {
    3. ......
    4. }
    5. static MemoryPool<MotherFucker> fuckerPool;
    6. // 和new效果类似,分以下几个步骤:
    7. // 1、从内存池中获得内存块
    8. // 2、对内存块进行构造(......为任意类型任意数量参数,传入构造函数,可以为空)
    9. // 3、返回
    10. auto shit = fuckerPool.newElement(......);、
    11. // 和delete效果类似:
    12. // 1、执行shit的析构
    13. // 2、回收内存
    14. fuckerPool.deleteElement(shit);

    这样设计的好处:
    1、避免了new/delete的性能消耗
    使用前,游戏的内存占用可能波动频率比较高,因为不断的new/delete,这里会有内存调度计算,还会内存碎片化。
    使用后,提前分配一块固定大小内存,满足不久的未来的使用,明显减少内存调度和内存碎片化。可能内存占用上会多一些。可以自定义内存管理算法,更高效。

    屏蔽new/delete函数(重载),全部走内存池,可以统一跟踪动态内存,避免内存泄露。

    不足
    一种对象类型一个内存池,通过template模板设计,可能会让代码比较臃肿。
    如果要彻底的字节为单位的内存池,那就要重载new/delete,根据EffectiveC++作者的建议是“别作”,设计难度比较大。举个例子,重载一个类的delete/new,子类也会调用这个new/delete,根据参数识别难度大。

    一次只能分配一个对象,无法做到new[]一样分配一个地址连续的数组,需要重载new/delete,设计成本与产出可能不成比例。
    可多次分配,也可以组成一个vector。

    实现原理
    1、std::allocator,
    2、new的定位形式,new(p) P();,用P的构造函数去构造p指针指向的内存,其实就是内存分配与构造分离。allocator的底层也是这个原理。