堆中连续分配对象的动态向量

Posted

技术标签:

【中文标题】堆中连续分配对象的动态向量【英文标题】:Dynamic vector of contiguously allocated objects in the Heap 【发布时间】:2021-07-08 06:16:08 【问题描述】:

我正在尝试构建一个类来管理 std::vector 指向必须在堆中连续分配的对象的指针。

我遇到的问题是构建如下图的向量会导致非连续分配的对象:

std::vector<T*> objects;
for (size_t i = 0; i < n; i++) objects.push_back(new T());

这是因为new T() 的两次连续执行不必在内存中产生连续的对象。所以解决方案是改用new[] 运算符,这让我接受了这个实现:

template<class T>
class HeapVector

private:
  std::vector<T*> elems_;
  const size_t BUFFER_SIZE_; // Buffer size
  size_t idx_; // Index controlling the index local to the buffer.
  T * buffer_ = nullptr; // Buffer

public:
  HeapVector() = default;
  HeapVector(const size_t BUFFER_SIZE = 256) : BUFFER_SIZE_(BUFFER_SIZE) ;


  void emplace_back() // TODO: Should pass constructor parameters or even a constructor
  
    if (!(elems_.size() % BUFFER_SIZE_))
    
      idx_ = 0;
      buffer_ = new T[BUFFER_SIZE_];
      elems_.reserve(elems_.size() + BUFFER_SIZE_);
    
    
    // TODO: Object constructor. Must initialize buffer_[idx]
    // createElement(buffer_[idx], parameters...);
    elems_.push_back(buffer_ + idx_++);
  

;

通过执行new T[BUFFER_SIZE_],我获得了一个指针,该指针指向使用默认构造函数构建的连续分配的 BUFFER_SIZE_ 元素数组的第一个元素。

我想要实现的是,在这个分配完成后,用所需的参数/另一个构造函数初始化这个对象(参见 TODOs)。另外,我想避免使用复制构造函数。

鉴于我希望此类成为模板类,实现这一目标的最通用方法是什么?

【问题讨论】:

听起来你可能想使用placement new。 "指向必须在堆中连续分配的对象的指针。" - 那么为什么要存储指针呢?为什么不std::vector&lt;T&gt; 我不明白为什么您需要创建更多对象只是因为您将它们直接存储在向量中而不是存储指向每个对象的指针。 "我不想混合使用 std::vector&lt;T&gt;std::vector&lt;T*&gt;" - 为什么?带有T 的那个将是拥有它的那个,其余的可以存储T*。只要拥有的vector 不需要重新分配它就应该是安全的,如果它必须重新分配,则需要重新创建指针向量。 补充@TedLyngmo 的评论:此外,如果您使用索引而不是指针,那么使用向量重新分配也是安全的。 【参考方案1】:

使用std::vector 作为对象池来存储对象并使用索引而不是指针来访问对象:

#include <iostream>
#include <vector>

struct Object 
    std::string name;
    std::size_t age;

    Object(const std::string &n, std::size_t a): name(n), age(a) 
;

template<typename T>
class Pool;

template<typename T>
class ObjectRef 
    Pool<T> &pool;
    std::size_t index;
    ObjectRef(Pool<T> &p, std::size_t i): pool(p), index(i) 
public:
    friend Pool<T>;
    T &operator*() 
        return pool.buffer[index];
    
    T *operator->() 
        return &pool.buffer[index];
    
;

template<typename T>
class Pool 
    std::vector<T> buffer;
public:
    friend ObjectRef<T>;
    Pool(std::size_t s = 250) 
        buffer.reserve(s);
    
    
    template<typename... Ts>
    ObjectRef<T> newObject(Ts ...args) 
        buffer.emplace_back(args...);
        return ObjectRef<T>(*this, buffer.size() - 1);
    
;

int main() 
    Pool<Object> objectPool(250);
    auto objectRef = objectPool.newObject("Name", 30);
    std::cout << objectRef->name;
    Pool<int> intPool(250);
    auto intRef = intPool.newObject(30);
    std::cout << *intRef;

【讨论】:

以上是关于堆中连续分配对象的动态向量的主要内容,如果未能解决你的问题,请参考以下文章

20170507

处理包含动态分配成员的对象向量

彻底销毁动态分配对象的向量

在删除指向动态分配对象的指针向量中的元素之前,我需要做啥?

在 cpp 中声明为静态的动态增长向量

C ++中动态分配的向量中的分段错误