views:

436

answers:

9

I'm porting a bit of an old code from C to C++. The old code uses object-like semantics, and at one point separates object destruction from freeing the now-unused memory, with stuff happening in between:

Object_Destructor(Object *me) { free(me->member1), free(me->member2) }

ObjectManager_FreeObject(ObjectManager *me, Object *obj) { free(obj) }

Is the above functionality possible in C++ using the standard destructor (~Object) and a subsequent call to delete obj? Or, as I fear, doing that would call the destructor twice?

In the particular case, the operator delete of Object is overridden as well. Is the definition I've read elsewhere ("when operator delete is used, and the object has a destructor, the destructor is always called) correct in the overridden operator case?

+3  A: 

The delete operator is used to free memory, it doesn't change whether the destructor is called or not. First the destructor is called, and only after that is the delete operator used to deallocate the memory.

In other words it's not possible to achieve the semantics you're aiming at with C++'s destructors and delete operators.

Sample:

#include <iostream>
#include <new>
using namespace std;

struct foo {
    ~foo() { cout << "destructor\n"; }
    void operator delete(void* p) { 
        cout << "operator delete (not explicitly calling destructor)\n"; 
        free(p);
        cout << "After delete\n"; 
    }
};

int main()
{
    void *pv = malloc(sizeof(foo));
    foo* pf = new (pv) foo; // use placement new
    delete pf;
}

Output:

destructor

operator delete (not explicitly calling destructor)

After delete

Motti
Thank you, but it isn't what I asked. Can the functionality seen in the C code (a destructor being separate from memory deletion) be implemented in C++ using a C++ destructor and operator delete (normal or overloaded)? Or the operator delete is always coupled with a call to the destructor?
MaxVT
My answer is Yes it's always coupled, or in other words, no you can't implement these semantics with a destructor and a delete operator.
Motti
It's always coupled. why would you need to decouple it though? The only reason I can think of is to have more than one way to delete an object.. but in that hypothetical case you wouldn't be able to rely on the language to know what destructor to automatically call when an object goes out of scope.
int3
You sometimes want to be able to delete an object and reuse its memory for a new object (with placement new), however it's never beneficial to release an objects memory without destroying it first (at least *I* can't think of such a scenario).
Motti
A: 

It sounds like you might want a placement new. On the other hand, it also sounds like your code is getting pretty hairy. It might be time for some heavy refactoring.

Steve S
Um, why the down vote? The question, as I understood it, was about separating memory deallocation from object destruction, and my answer is relevant to that.
Steve S
My guess is that you were down-voted because placement new is relevant for separating construction and memory allocation, the question was about separating destruction and memory _de_allocation. As I said in the comment to the accepted answer, you can have destruction without deallocation but you can't have deallocation without destruction (unless you choose to subvert the type system by using `char` buffers).
Motti
As you say, you have to subvert the type system. It does work. Incidentally, my understanding was that typically you do separate destruction and deallocation when you use placement new. Check out the link in my answer.
Steve S
+3  A: 

Overloaded delete still calls destructor implicitly before it starts executing as opposed to placement delete (but placement delete is not supposed to be called directly). So if you are going to "delete" object, do not destroy it in advance you will have destructor called twice. However explicit destruction is due if object was created with placement new (but in that case you do not destroy object using delete)

BostonLogan
+1  A: 

You can separate destruction from deletion, but you probably don't really want to.

If you allocate the memory with new char[] or malloc, and then call placement new, then you can separate destruction (which you do by directly calling the destructor) from deletion (or free). But then you're no longer calling the class's overloaded operator delete, instead you're calling delete[] on the char array (or free).

If you call delete via a pointer to your class (the one you overloaded operator delete for), then that class's destructor will be called. So there is no way to separate them in the sense you ask for, of calling delete without the destructor.

Steve Jessop
Actually you *can* destroy an object without freeing the memory (by explicitly calling the dtor `p->~Type`) you can't do the opposite, free the memory without destroying the object.
Motti
OK, you could explicitly call the dtor, then explicitly call operator delete. But I don't think that's defined behaviour on an object allocated with `new` for that class. When I said "you can't", I meant "validly". I could be wrong about it not being valid, of course.
Steve Jessop
Thanks for the explanation.
MaxVT
Steve, if you explicitly call the destructor and then call delete it will cause the destructor to run twice which is undefined. If you call the destructor, then call placement new on that memory chunk and *then* call delete, you're fine since each constructed object's destructor is called exactly once.
Motti
OK, you're right, I got carried away. When the questioner said "stuff happening in between", I think he meant "there is some stuff already in the existing code", not "does there exist stuff I could do that would make this valid?". But I was wrong to say that C++ considers this illegitimate - the language doesn't, it's just that you have problems with with exception-safety. If placement new throws an exception then you're out of options, since you can't re-construct the object, and you can't delete it either.
Steve Jessop
+1  A: 

No, it is not possible.

delete calls the destructor.

You will need to work out some kind of logic to ensure that Stuff happens in the right order.

Paul Nathan
A: 

Have a look at the std::allocators for the implementation. The answer is 'yes, they may be decoupled'. It's quite easy to do, it just not seen very often.

Justin
+2  A: 

What sort of stuff happens between the destruction of the object and the freeing of the object's memory? If it has nothing to do with the object, then you should be able to delete the object where the destructor appears. If it does, well, I'd examine that very carefully, because it sounds like a bad idea.

If you have to reproduce the semantics, have a member function that releases all the resources, and use that instead of the destruct function. Make sure that function can be called more than once safely, and include it in the C++ destructor just to be sure.

David Thornley
+1 for having actually read the question.
Steve S
A: 

I absolutely don't get why people say it's impossible.

Decoupling initialization from construction and zeroization (tm) from destruction is actually extremely simple.

class Clike
{
public:
  Clike() : m_usable(true) {}

  void clear(); // performs clean up then sets m_usable to false
  ~Clike() { if (m_usable) this->clear(); }

private:
  bool m_usable;
  // real variables
};

Then you can use it like so:

Clike* c = new Clike();

c->clear(); // performs cleanup

// stuff

delete c;

Actually, since destructors should never throw and do not return anything, it is not unusual at all that the cleanup and the destruction be separated so that the cleanup operation may report errors. Especially for complicated beasts like DB Connections etc...

While this is not a 'destructor' thing, it sure works, and so the C-code presented is actually perfectly reproducible without those fancy placement new etc...

Matthieu M.
A: 

The destructor is coupled to delete and you can't call it twice because you can't explicitely call the destructor (or at least it is highly unusual and uncommon, I've never seen it).

However, just make object_destructor() a member function and call it explicitely (and usually it's good style to make it safe for being called twice. In your case however, calling twice is okay anyway, because calling free() with a NULL pointer is legal, so the alternate version of object_destructor is just to highlight how it could be done.

CLASS A {
   object_destructor() { free(this->member1); free(this->member2); }

   alternate_version_of_object_destructor() {  
           if (this->member1) { free(this->member1); this->member1= NULL; } 
           if (this->member2) { free(this->member2); this->member2= NULL; } }

    ~A() { /* do nothing or just call this->object_destructor() for safety */ }
}


foo() {
    A *pa= new A;


    pa->object_destructor();  /* member cleanup */
    delete pa;                /* free object memory */
}
Nicholaz