exTrace101
exTrace101

Reputation: 104

c++ delete should do nothing

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

Answers (4)

James Kanze
James Kanze

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

Cheers and hth. - Alf
Cheers and hth. - Alf

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

jaor
jaor

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

Cheers and hth. - Alf
Cheers and hth. - Alf

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

Related Questions