2013-02-05 40 views
2

当前在我写过的库中,我的小对象(不是多态的)在对象池中分配给一个带有unique_ptr的向量。现在显然我想改变这种情况,因为很多次呼唤新人显然有很多头痛的问题。我很好奇它是否更有效:缓存池中的对象(将其存储在向量中,即vector<Object>),或者在需要时通过它的ID创建对象。请注意,有很多创建的对象。在堆栈上分配一个小对象(比如在堆上创建一次)会更有效率吗?

我的意思是,我应该这样做:

需要时创建对象? (注意这些对象是小的,64-128位,因为所有被包含是一个ID和一个参考/指针到父对象)

Object ObjectFactory::create() 
{ 
    return Object(nextId(), getParent()); 
} 

Object ObjectFactory::get(unsigned id) 
{ 
    return Object(id, getParent()); 
} 

或:

Object& ObjectFactory::create() 
{ 
    // get the next id of the object 
    unsigned id = nextId(); 

    // resize (if necessary) 
    if(_objects.size() <= id) 
    { 
     _objects.resize(id + 1); 
     _objects[id]._parent = getParent(); 
    } 

    return _objects[id]; 
} 

Object& ObjectFactory::get(unsigned id) 
{ return _objects[id]; } 

我什么特别关注的是:会重新创建的Object的原因是多少?

+2

有一个从这个问题一个失踪的细节:当您运行两个版本,一个是太多的开销? –

+2

让我们暂时假设堆比堆栈慢100倍。你的应用程序会经常这样做吗,它会注意到这种差异?如果没有,有一个干净,易于使用和维护界面不是更重要吗? – PlasmaHH

+2

将对象存储在连续内存中(std :: vector保证它)可以为您带来巨大的速度提升,而不是将它们存储在内存碎片区域。 – borisbn

回答

0

@LokiAstari是正确的,你很明显有你的指针调整大小的问题。

有些事我不明白;你说你正在使用一个对象池,但是你有太多新语句的问题。如果你使用的是对象池,我会说这正是为了避免新的陈述,不是吗?

这里是我的建议,虽然我不是专家,可能会有更好的解决方案,通常包括执行自己的分配器(力量的黑暗面)。你可以使用像std::deque这样的容器,它确保指针/引用对调整大小的有效性。

您将开始调整大量对象(您的池)的初始大小,并且可以在需要时手动处理后续调整大小(以预定义大小的块扩展容量),或接受新语句,然后if你知道不应该有很多,并使用emplace_back方法。

我也不知道你是否做了很多插入/删除对象与你的ID。如果是这样,你可以考虑使用std::unordered_map

下面是一个例子使用std::deque

#include <iostream> 
#include <deque> 

#define POOL_RESERVE 1000 


// Data storage for your object 
struct MyObjectData 
{ 
    double some_data; 
}; 


// Container returned by the factory 
struct MyObject 
{ 
    typedef MyObjectData data_type; 

    unsigned id; 
    data_type* data; 

    MyObject(): id(0), data(0) {} 
    MyObject(const unsigned& id_, data_type* data_): id(id_), data(data_) {} 

    void set(const unsigned& id_, data_type* data_) 
     { id = id_; data = data_; } 
}; 


// MyObject Pool 
class MyObjectPool 
{ 
public: 

    typedef MyObjectData data_type; 

    MyObjectPool(): count(0) { pool.resize(POOL_RESERVE); } 

    void get(const unsigned& id, MyObject& obj) 
     { 
      // Check requested index 
      if (id >= count) 
       obj.set(0, 0); 
      else 
       obj.set(id, &pool[id]); 
     } 

    void create(MyObject& obj) 
     { 
      // Create new data container if needed 
      if (count++ >= pool.size()) pool.emplace_back(); 

      // Return next available object 
      obj.set(count-1, &pool[count-1]); 
     } 

private: 

    unsigned count; 
    std::deque<data_type> pool; 
}; 


// MyObject factory 
class MyObjectFactory 
{ 
    typedef MyObjectFactory self; 
    static MyObject local; 

    static MyObjectPool& get_instance() 
     { 
      static MyObjectPool pool; 
      return pool; 
     } 

public: 

    static MyObject get(const unsigned& id) 
     { 
      self::get_instance().get(id,local); 
      return local; 
     } 

    static MyObject create() 
     { 
      self::get_instance().create(local); 
      return local; 
     } 
}; 

// Define static variable 
MyObject MyObjectFactory::local = MyObject(); 


// Usage example 
int main() 
{ 
    MyObject a,b,c; 

    a = MyObjectFactory::create(); 
    b = MyObjectFactory::create(); 
    c = MyObjectFactory::get(1); 
} 
+0

为什么在'std :: vector''上使用'std :: deque''?我想我可以使用我选择使用的第二个选项。顺便说一句,我不认为你需要使用单身,只是我的意见。 –

+0

正如我所说的,'std :: vector'不能确保'resize'或'push_back'之后的指针/引用的有效性,这就是为什么你需要使用'std :: deque'。我认为单身人士是必要的,因为一个游泳池应该是可以访问的,而不必明确提及工厂(这种方式不明确和危险)。任何函数或方法应该能够先验地访问池的对象,即使我不知道你的程序。如果您的工厂意外被抄袭会发生什么?你读过@LokiAstari的评论吗?你的第二个解决方案是无效的。 – Sheljohn

+0

@ Sh3john,我读它..只是不知道它调整大小和push_back内部对象。 –

相关问题