Reputation: 104
Question:
is there any way to make delete behave like a dummy i.e do nothing when called?
Why I need the answer:
I'm using a custom memory pool that frees the all the object from memory when a static method is called Pool::freeAllObjects()
.
all the classes have an overloaded operator new like the following:
void * operator new (size_t size)
{
void * obj = Pool::memory()->allocate(size);
Pool::memory()->save_destructor((ClassName*)obj);
return obj;
}
Pool::memory()->save_destructor()
just saves a pointer to function that runs the destructor of a generic type.
if no one calls delete
on the objects that are created with the pool then everything behaves correctly but in the code we want to use it there are many instances of objects which have delete
called on, so for backward compatibility I tried to make an overloaded delete
like this
void operator delete(void*) {/*do nothing*/}
in the classes that have the overloaded new
using the Pool::memory()
, but it looks like that did not solve the problem. I used a simple std::cout to see the cons/destructor called and an overloaded delete like:
void operator delete(void*) {std::cout << "deleting ClassName" << std::endl;}
so in a code like this:
ClassName * instance = new ClassName();
instance->runMethod();
delete instance; //this is legacy code calling delete before the pool was developed
/*
other code goes here
*/
Pool::freeAllObjects(); // oops instance was already deleted
I get the following output:
constructing ClassName
<-- constructor called
destructing ClassName
<-- destructor called because of calling delete
deleting ClassName
<-- delete displaying its message
destructing ClassName
<-- destructor called because of the Pool::freeAllObjects running the destructor on the object
EDIT: sorry that I didn't mention that the requirement was not to use smart pointers and making operator delete private (which I think is the best option by the way) is also not allowed.
Upvotes: 2
Views: 469
Reputation: 154047
You can't prevent delete
from calling the destructor; that's
what it does. But since you're catching the operator delete
function which is called later, you can use some sort of hidden
flag. Something like:
union MemoryHeader
{
bool hasBeenDeleted;
double forAlignment;
};
void* operator new ( size_t size )
{
MemoryHeader* hdr = static_cast<MemoryHeader*>(
Pool::memory()->allocate( size + sizeof( MemoryHeader ) ) );
hdr->hasBeenDeleted = false;
void* obj = hdr + 1;
Pool::memory()->save_destructor( (ClassName*)hdr );
return obj;
}
void operator delete( void* obj )
{
MemoryHeader* hdr = static_cast<MemoryHeader*>( obj ) - 1;
hdr->hasBeenDeleted = true;
}
Then, when you run your deleters, you can check the flag.
Or perhaps even better, in your case; in your operator delete
function, just deregister the destructor for the object: add
a clear_destructor
function to what Pool::memory()
returns,
and call it.
Upvotes: 2
Reputation: 145457
An alternative you might consider is to disable use of delete
for the relevant classes.
Simply make the destructor protected
or private
, and add the pool allocator as friend
.
This enforces the existing design, instead of either supporting incorrect use (one other answer) by adding more complexity, or changing the design to something more simple and clean (my other answer).
Upvotes: 0
Reputation: 881
If you want to avoid destructor being called automatically you'd have to implement placement new
operator, in case of which destructor must be called explicitly ( refer to C++ FAQ ).
If you want to disable freeing memory by delete
statement, overloading it is a good idea, either globally (if you know what you're doing) or per-class (use inheritance maybe. new
/delete
operators are inherited).
It all depends on what you want to achieve and what compatibility to existing system you must sustain.
Either way - it's do-able. Other question is - is that the way it should be done.
Upvotes: 0
Reputation: 145457
operator delete
is a deallocation function.
A delete expression calls the destructor first, then the relevant deallocation function.
"Pool::memory()->save_destructor() just saves a pointer to function that runs the destructor of a generic type." is ungood design. Instead, if deallocation responsibility is to be shared, then handle that at a higher level by using std::shared_ptr
(you can restrict your classes to only be instantiable via a function that produces a std::shared_ptr
). That's what std::shared_ptr
is for.
With the allocator responsible for destroying objects, high and low level concerns are conflated, which results in complexity and probably bugs.
With those concerns separated, e.g. by using std::shared_ptr
, you get a simpler and more reliable design, which also will be more in line with such separation in the C++ language.
The classic book “Modern C++ Design” by Andrei Alexandrescu discusses in detail how to implement a custom allocation scheme. Chances are that you can find that allocator in the Loki library. I believe that it's open source.
If you want a Java-like automatic garbage collection then you might be able to use the Boehm collector. I know some folks have used it successfully on medium to large projects. However, I have no direct experience, but check it out if that's what you're aiming for.
Upvotes: 1