Search code examples
c++c++11memory-managementpool

How to create pool of contiguous memory of non-POD type?


I have a scenario where I have multiple operations represented in the following way:

struct Op {
  virtual void Run() = 0;
};

struct FooOp : public Op {
  const std::vector<char> v;
  const std::string s;
  FooOp(const std::vector<char> &v, const std::string &s) : v(v), s(s) {}
  void Run() { std::cout << "FooOp::Run" << '\n'; }
};

// (...)

My application works in several passes. In each pass, I want to create many of these operations and at the end of the pass I can discard them all at the same time. So I would like to preallocate some chunk of memory for these operations and allocate new operations from this memory. I came up with the following code:

class FooPool {
public:
  FooPool(int size) {
    foo_pool = new char[size * sizeof(FooOp)]; // what about FooOp alignment?
    cur = 0;
  }

  ~FooPool() { delete foo_pool; }

  FooOp *New(const std::vector<char> &v, const std::string &s) {
    return new (reinterpret_cast<FooOp*>(foo_pool) + cur) FooOp(v,s);
  }

  void Release() {
    for (int i = 0; i < cur; ++i) {
      (reinterpret_cast<FooOp*>(foo_pool)+i)->~FooOp();
    }
    cur = 0;
  }

private:
  char *foo_pool;
  int cur;
};

This seems to work, but I'm pretty sure I need to take care somehow of the alignment of FooOp. Moreover, I'm not even sure this approach is viable since the operations are not PODs.

  • Is my approach flawed? (most likely)
  • What's a better way of doing this?
  • Is there a way to reclaim the existing memory using unique_ptrs?

Thanks!


Solution

  • I think this code will have similar performance characteristics without requiring you to mess around with placement new and aligned storage:

    class FooPool {
    public:
        FooPool(int size) {
            pool.reserve(size);
        }
    
        FooOp* New(const std::vector<char>& v, const std::string& s) {
            pool.emplace_back(v, s); // in c++17: return pool.emplace_back etc. etc.
            return &pool.back();
        }
    
        void Release() {
            pool.clear();
        }
    
    private:
        std::vector<FooOp> pool;
    }
    

    The key idea here being that your FooPool is essentially doing what std::vector does.