Ok, so I was bored today and decided to write a simple memory pooling demo, where I have a singleton memory_pool object, and then the clients of the memory pool make use of it by overloading new and delete. The memory_pool exists on a per type basis, which is done with templates.
So first I have a general question about singletons, and second I was looking for general feedback, in terms of if my memory_pool object is implemented incorrectly, could be doing something better, etc. Also I've never actually overloaded new/delete before, so I was wondering if there were special issues I should be aware of.
Question 1: How do I get the singleton destructor to fire? As far as I can tell, it never will fire, because I would need to explicitly call delete against my instance_ pointer. Is there a standard way to do this?
Question 2: Is there any improvements I can make, or did I implement anything incorrectly? There are two shortcomings that I can see right away: a) Not thread-safe (easy to fix); b) new[] and delete[] are not handled by the memory_pool, so global new[] and delete[] would be used (not so easy to fix, the implementation would probably have to be completely different).
Ok, here's the code:
Code:
# include <new>
# include <list>
# include <algorithm>
# include <iostream>
using namespace std;
template <class T, int N = 100>
class memory_pool {
class delete_it {
public:
void operator()(char* p) {
cout << "Deleting memory at " << p << endl;
delete[] p;
}
};
memory_pool() {
// preallocate N objects
alloc_n();
}
void alloc_n() {
for (int i = 0; i < N; i++) {
char* p = new char[sizeof(T)];
cout << "Allocating memory at " << reinterpret_cast<T *>(p) << endl;
pool_.push_back(p);
}
}
public:
virtual ~memory_pool() {
// destroy each object still inuse
while (!inuse_.empty()) {
char* p = inuse_.front();
Destroy(p);
}
// delete all pooled objects
for_each(pool_.begin(), pool_.end(), delete_it());
}
static memory_pool<T,N>* Instance() {
if (!memory_pool<T,N>::instance_) memory_pool<T,N>::instance_ = new memory_pool<T,N>();
return memory_pool<T,N>::instance_;
}
T* Create() {
if (pool_.empty()) {
// allocate N more objects
alloc_n();
}
// remove element from available pool
char* p = pool_.front();
pool_.pop_front();
// put onto inuse list
inuse_.push_back(p);
return reinterpret_cast<T*>(p);
}
private:
void Destroy(char* p) {
// remove element from inuse pool
inuse_.remove(p);
// add back to available pool
pool_.push_back(p);
}
public:
void Destroy(T* p) {
Destroy(reinterpret_cast<char *>(p));
}
static memory_pool<T,N>* instance_;
private:
list<char*> pool_;
list<char*> inuse_;
};
class Demo {
int x;
public:
Demo() {
x = 0;
cout << "Constructing Demo " << this << endl;
}
~Demo() {
cout << "Destructing Demo " << this << endl;
}
void* operator new(size_t sz);
void operator delete(void* p, size_t sz);
};
typedef memory_pool<Demo,5> DemoPool;
DemoPool* DemoPool::instance_ = 0;
void* Demo::operator new(size_t sz) {
return DemoPool::Instance()->Create();
}
void Demo::operator delete(void* p, size_t sz) {
DemoPool::Instance()->Destroy(static_cast<Demo *>(p));
}
int main() {
for (int i = 0; i < 10; i++) {
Demo* d = new Demo();
delete d;
}
return 0;
}