首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++对象池

C++对象池
EN

Code Review用户
提问于 2016-06-16 18:14:25
回答 1查看 2.2K关注 0票数 5

我是一个初学者,想要建一个游泳池。我做了一些研究,发现如果我的软件有沉重的对象创建和删除部分,我应该使用池,因为我的性能。我做了一些功能,从池中获取一个T,并重新设置它,并想知道它是否正确工作。我尝试了我的实现,但似乎有点慢。我怎样才能改善这一点?在数组中在堆栈上分配Ts的池,每个正在使用的对象都有一个存储的指针,空闲()通过指针重置对象。我也做了一个完全重置的功能。我发现我应该使用一个智能指针,但首先我想用自己的指针构建一个简单的池。作为第一个改进,我想为向量使用一个新的位置,以避免使用堆。

代码语言:javascript
复制
#include <array>
#include <vector>
#include <algorithm>
#include <iostream>

//------------------------------------------------------------------------------

template<class T,int N>
class Pool
{
public:
    Pool();

    T* get();
    void free(T* t);
    void reset();
    int available()const{ return N - used.size(); } //   number of free Ts

private:
    T default_T{};    //  reset helper
    std::array<T,N> allocated{};
    std::vector<T*> used;
};
//------------------------------------------------------------------------------

template<class T, int N>
T* Pool<T,N>::get()
//  get a T from the pool;  return 0 if no free Ts
{
    if(available()<1)
    {
        std::cerr << "Pool exhausted! Free memory!\n";
        return 0;
    }

    T* t{nullptr};
    for(unsigned int i=0; i<allocated.size(); ++i)
    {
        auto p = std::find(used.begin()+i,used.end(),&allocated[i]);
        if(p==used.end())
        {
            t = &allocated[i];
            used.push_back(t);
            return t;
        }

    }

    return t;
}

//------------------------------------------------------------------------------

template<class T,int N>
void Pool<T,N>::free(T* t)
//  return a T given out by get() to the pool and reset it to default
{
    auto p = std::find(used.begin(),used.end(),t);
    if(*p==t)
    {
        *t = default_T;
        used.erase(p);
    }
}

//------------------------------------------------------------------------------

template<class T,int N>
void Pool<T,N>::reset()
//  return ALL T given out by get() to the pool
{
    for(auto& r: allocated)
        free(&r);
}
EN

回答 1

Code Review用户

发布于 2016-06-17 20:09:29

概述

我要指出的第一点是,所有容器都可以定制为使用替代分配器(比如池分配器)。因此,我将尝试编写一个使用池的标准分配器,而不是编写一些非常定制的东西。

第二点。对于创建和销毁小对象,C++进行了高度优化。除非您的类非常特殊,否则标准的分配器将非常快,击败它们将是困难的。

设计评论:

您的分配器在分配和取消分配中都会在整个池中进行线性搜索。坦率地说,这是相当糟糕的,你的分配时间只会变得更糟,因为你的池变大了。

还可以返回已经构造的对象。当对象被释放时,它们不会被销毁(因此您得到的对象状态可能是不确定的)。我见过的大多数分配器系统都会给出一些正确对齐的原始字节,并期望您返回原始字节(即已调用了析构函数)。在返回对象之前,不能调用析构函数,因为get返回已构造的对象。

代码评审

不要写这样的评论

代码语言:javascript
复制
//  get a T from the pool;  return 0 if no free Ts

我想我可以从名字中看出这一点。你的评论应该解释为什么不是什么。我应该能够从写得好的代码中看出这一点。问题在于,如果代码和注释描述的是什么,那么随着时间的推移(当应用bug修复时)可能会出现差异。如果你解释一下为什么通常情况会保持不变。

如果我现在将代码更改为返回nullptr,那么注释和代码是不同的,但它仍然有效。下一个人是否更改代码或注释。

错误码

代码语言:javascript
复制
        // I would throw an exception
        std::cerr << "Pool exhausted! Free memory!\n";
        return 0;

错误代码在包含有自己的单元测试的类中很好。但是,逃避接口边界的错误代码是个坏主意。它假设使用您的代码的人与您一样优秀,并将检查错误代码并采取适当的操作(提示:他们不会)。抛出一个异常。这样,如果他们忘记检查错误,代码就不会中断。

复杂性

这看起来像一个O(N^2)循环。

代码语言:javascript
复制
    T* t{nullptr};
    for(unsigned int i=0; i<allocated.size(); ++i)
    {
        auto p = std::find(used.begin()+i,used.end(),&allocated[i]);
        // STUFF
    }

为什么不只是一个find()呢?

更好的是为什么要搜索。有一份免费的名单。将标题项目从空闲列表中删除并返回。当返回一个值时,将其添加回空闲列表的首。

注意:我不是在说一个明确的列表结构。您可以拥有一个对象数组。以及一系列的索引值。然后,空闲项数组包含列表中的下一个元素。

代码语言:javascript
复制
// Example of a free list.

std::array<T, N>    objects;
std::array<int, N>  freeList;
int freeListHead;

Pool()
   : freeListHead(0)
{
    for(int loop = 0; loop < N; ++loop)
    {
        freeList[loop] = loop + 1;
    }
}
T* get()
{
    T* result = &objects[freeListHead];
    freeListHead = freeList[freeListHead];
}
void put(T* obj)
{
    int offset = std::distance(&objects[0], obj);
    freeList[offset] = freeListHead;
    freeListHead = offset;
}

假设T

的性质

您假设T有一个copy assignment operator。在这个充满可移动物体的美丽的新世界里,情况并非如此。

代码语言:javascript
复制
        *t = default_T;

重置中的

复杂性.

代码语言:javascript
复制
void Pool<T,N>::reset()
{
    for(auto& r: allocated)
        free(&r);
}

这似乎是合乎逻辑的。但是free()包含一个find()。因为您需要重置所有项目,所以您可以先这样做,然后jsut重置包含迭代器的容器,然后就会产生相同的影响。

代码语言:javascript
复制
void Pool<T,N>::reset()
{
    for(auto& r: allocated)
        *r = default_T;
    allocated.reset();
}
票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/132224

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档