The purpose of this class is to wrap a std::vector in a class so that never a new object is added. We don't allocate a new object on the stack but we trying to fit it on the current memory page, or create a new page for it.
#include <vector> template<class T> class PagedVector { public : PagedVector(int initialCapacity, float load_factor = 1.5f): pageSize(static_cast<size_t>(load_factor*initialCapacity*sizeof(T))), current_byte_offset(0), current_page(0) { data.reserve(initialCapacity); //initialise one page: char* ptr = static_cast<char*>(malloc(pageSize)); pages.push_back(std::make_pair(ptr, pageSize)); } template<class U> U* create() { char* memoryPtr = getNextAdress(sizeof(U), std::alignment_of<U>::value); U* objectPtr = new (memoryPtr) U(); data.push_back(objectPtr); return objectPtr; } ~PagedVector() { //delete objects for(T* obj : data) { obj->~T(); } //free memory for(auto& pair : pages) { free(pair.first); } } void reset() { //delete objects for(T* obj : data) { obj->~T(); } data.clear(); //keep memory but reset page count current_page = 0; current_byte_offset = 0; } const std::vector<T*>& getData() const { return data; } private : std::vector<T*> data; std::vector< std::pair<char* , size_t > > pages; size_t pageSize; size_t current_byte_offset; int current_page; char* getNextAdress(size_t size, size_t align) { //get next available adress for type with given size and aligment char* current_page_adress = pages[current_page].first + current_byte_offset; char* end_page_adress = pages[current_page].first + pages.back().second; size_t remainder_align = reinterpret_cast<uintptr_t>(current_page_adress) % align; size_t extra_alignment_padding = remainder_align==0 ? 0 : (align - remainder_align); char* next_aligned_adress = current_page_adress + extra_alignment_padding; char* end_aligned_adress = next_aligned_adress + size; if(end_aligned_adress < end_page_adress) { current_byte_offset += size + extra_alignment_padding; return next_aligned_adress; } //no more space on the current page, have to reallocate a new one: //in the rare case where the object size > page size size_t nextPageSize = std::max(pageSize, size); char* ptr = static_cast<char*>(malloc(nextPageSize)); pages.push_back(std::make_pair(ptr, nextPageSize)); current_byte_offset = size; ++current_page; return ptr; } //non-copiable: PagedVector(PagedVector&); PagedVector& operator=(PagedVector&); }; Usage:
PagedVector<Base> pv(5); pv.create<Base>(); Derived* p = pv.create<Derived>(); I'm wondering if this is correct (not relying on UB). I've never dealt that much with placement new and manual memory management.
vector backed by pages. If you want to write something specific to you call it something else as the curent description is not what you are doing.Using vector to store the location of a set of pages so I can do global deallocation\$\endgroup\$