Re: Defining placement new[] and delete[]
On Dec 22, 11:22 pm, zr <zvir...@gmail.com> wrote:
Hi,
FAQ 11.14 (http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.14)
shows how to write placement new operator that uses a custom memory
pool. For the given example, how would the corresponding new[] and
delete[] operators be defined?
I tried adding my own implementations of:
void* operator new[](size_t nbytes),
void* operator new[](size_t nbytes, Pool& pool) and
void operator delete[](void* p)
{{start of code}}
#include <iostream>
class Pool {
public:
Pool(size_t _poolSize)
{
#if _DEBUG
std::cout << __FUNCTION__ << " Pool address = " << this << "\n";
#endif
nextAvail = start = (char*)malloc(_poolSize);
if (NULL==start)
{
throw std::bad_alloc();
}
end = start + _poolSize;
}
~Pool() {
free(start);
}
void* alloc(size_t nbytes) {
if (checkAllocations)
{
if (nbytes > avail())
{
throw std::bad_alloc();
}
}
char* pos = nextAvail;
nextAvail += nbytes;
return pos;
}
void dealloc(void* p) {
// delloc currenty does nothing
}
size_t avail() const {
return end-nextAvail;
}
size_t allocated() const {
return nextAvail-start;
}
protected:
char* start;
char* end;
char* nextAvail;
static const bool checkAllocations=true;
};
void* operator new(size_t nbytes)
{
if (nbytes == 0)
nbytes = 1; // so all alloc's get a distinct
address
void* ans = malloc(nbytes + 4); // overallocate by 4 bytes
*(Pool**)ans = NULL; // use NULL in the global new
return (char*)ans + 4; // don't let users see the Pool*
}
void* operator new[](size_t nbytes)
{
if (nbytes == 0)
nbytes = 1; // so all alloc's get a distinct
address
void* ans = malloc(nbytes + 4); // overallocate by 4 bytes
*(Pool**)ans = NULL; // use NULL in the global new
void* res = (char*)ans + 4;
#if _DEBUG
std::cout << __FUNCTION__ << "returned=" << res<< "\n";
#endif
return res; // don't let users see the Pool*
}
void* operator new(size_t nbytes, Pool& pool)
{
if (nbytes == 0)
nbytes = 1; // so all alloc's get a distinct
address
void* ans = pool.alloc(nbytes + 4); // overallocate by 4 bytes
*(Pool**)ans = &pool; // put the Pool* here
void* res = (char*)ans + 4;
#if _DEBUG
std::cout << __FUNCTION__ << "returned=" << res<< "\n";
#endif
return res; // don't let users see the Pool*
}
void* operator new[](size_t nbytes, Pool& pool)
{
if (nbytes == 0)
nbytes = 1; // so all alloc's get a distinct
address
void* ans = pool.alloc(nbytes + 4); // overallocate by 4 bytes
*(Pool**)ans = &pool; // put the Pool* here
void* res = (char*)ans + 4;
#if _DEBUG
std::cout << __FUNCTION__ << " returned=" << res<< "\n";
#endif
return res; // don't let users see the Pool*
}
void operator delete(void* p)
{
#if _DEBUG
std::cout << __FUNCTION__ << " freed=" << p;
#endif
if (p != NULL) {
p = (char*)p - 4; // back off to the Pool*
Pool* pool = *(Pool**)p;
if (pool == NULL)
{
free(p); // note: 4 bytes left of the original
p
#if _DEBUG
std::cout << " from general pool\n";
#endif
}
else
{
pool->dealloc(p); // note: 4 bytes left of the original
p
#if _DEBUG
std::cout << " from pool " << pool << "\n";
#endif
}
}
}
void operator delete[](void* p)
{
#if _DEBUG
std::cout << __FUNCTION__ << " freed=" << p;
#endif
if (p != NULL) {
p = (char*)p - 4; // back off to the Pool*
Pool* pool = *(Pool**)p;
if (pool == NULL)
{
free(p); // note: 4 bytes left of the original
p
#if _DEBUG
std::cout << " from general pool\n";
#endif
}
else
{
pool->dealloc(p); // note: 4 bytes left of the original
p
#if _DEBUG
std::cout << " from pool " << pool << "\n";
#endif
}
}
}
class A {
public:
A()
{
#if _DEBUG
std::cout << __FUNCTION__ << "\n";
#endif
}
~A()
{
#if _DEBUG
std::cout << __FUNCTION__ << "\n";
#endif
}
};
// Allocates an array using placement new[]
// and then throw an exception
class Bomb {
public:
Bomb(Pool& pool) {
#if _DEBUG
std::cout << __FUNCTION__ << "\n";
#endif
a = new(pool) A[4];
std::cout << "Now comes the exception...\n";
throw std::bad_alloc();
}
~Bomb() {
#if _DEBUG
std::cout << __FUNCTION__ << "\n";
#endif
delete [] a;
}
private:
A* a;
};
int _tmain(int argc, _TCHAR* argv[])
{
try {
Pool pool(1024*1024);
Bomb boom(pool);
} catch (std::bad_alloc& e)
{
std::cout << "A::A~ should have appeared above\n";
std::cout << e.what() << std::endl;
}
return 0;
}
{{end of code}}
The compiler gives the following warning:
warning C4291: 'void *operator new[](size_t,Pool &)' : no matching
operator delete found; memory will not be freed if initialization
throws an exception
d:\bufferpool\bufferpool.cpp(93) : see declaration of 'operator new[]'
and sure enough the allocated array was not auto-magically destroyed
as seen in the output:
Pool::Pool Pool address = 0012FF48
Bomb::Bomb
operator new[] returned=00420044
A::A
A::A
A::A
A::A
Now comes the exception...
A::A~ should have appeared above
So, is it possible to fix the above code to auto-magically destroy the
array after the execption occurs?