Reputation: 490
Let's say that for whatever reason I wanted to call the destructor on an object without freeing memory. I understand this is doable with obj->~Type()
, but let's say I have a void*
and don't know the type statically. I guess then I would need 2 pointers, one for the object and one for the destructor.
However, I am curious if it is possible to get away with just one pointer in this case. Let's say that I can guarantee that the object will have a virtual destructor, and it will derive at most from one base class. Is it possible to reach the destructor through the v-table? And is that doable in a standard-compliant way (probably not)?
Upvotes: 0
Views: 875
Reputation: 2866
You cannot get the address of a destructor. Quote from the C++17 standard:
The address of a destructor shall not be taken.
— See C++17 Latest Draft, [class.dtor]/2.
However your two-pointer approach works. See the following example (live):
class Apple {};
struct Destructor {
public:
template <class T>
Destructor (const T* object) :
object (object),
dtor ([] (const void* o) { static_cast<const T*> (o)->~T (); })
{
}
void Call ()
{
dtor (object);
}
private:
const void* object;
void (*dtor) (const void*);
};
int main ()
{
Apple* a = new Apple ();
Destructor d (a);
d.Call ();
}
The idea is taken from Herb Sutter's blog post. Remark: the memory pointed to by a
is not freed in my example.
For the above to work you will have to know type Apple
statically when you store the destructor. This is done when d
is constructed. The type Apple
is passed to the ctor. through template parameter T
.
But you do not have to know Apple
when you destroy it with d.Call ()
.
You can just as well call the destructor from an std::function
.
#include <functional>
class Apple {};
int main ()
{
Apple* a = new Apple ();
std::function<void ()> d = [=] () { a->~Apple (); };
d ();
}
Basically this does the same thing, as the previous approach. The pointer a
is captured by the lambda, so it knows which Apple
to destroy.
As previously when you are creating d
you have to know Apple
. It is in the lambda. If you call d ()
, you do not have to know the type.
struct Fruit { virtual ~Fruit () {} };
struct Apple : Fruit {};
Obviously you can call a dtor. of an Apple
through a Fruit*
pointer, if Fruit::~Fruit
is virtual
(otherwise it is UB). Similarly to the two-pointer solution, when you construct an object of Apple
to be stored and destroyed later, you have to know the type (Fruit* f = new Apple ()
), and when you destroy it, you do not have to know it (f->~Fruit ()
).
Here f
is a single pointer, but ends up using a second pointer, the vfptr (i.e. virtual function pointer), which points to an array of function pointers, one of which is the pointer to ~Apple
. So three pointers are dereferenced for f->~Fruit ()
:
f
,~Apple
.It is impossible. Calling a destructor is a call
instruction in assembly. For this you have to have two pointers:
this
pointer), andSo even if someone has a one-pointer solution, it contains the second pointer through some indirection.
Upvotes: 1
Reputation: 180295
Let's assume a reasonable compiler which puts the dtor last in the vtable. After all, the dtor is rarely called - only once per object. You can't find it because the various vtables have different lengths.
Upvotes: 1