c++ delete should do nothing

允我心安 提交于 2019-12-13 01:54:25

问题


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.


回答1:


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.




回答2:


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.




回答3:


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.




回答4:


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).



来源:https://stackoverflow.com/questions/23744531/c-delete-should-do-nothing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!