Rants on software, computing, and any other topic I feel like.

Thursday, February 17, 2011

Calling a constructor or destructor without an object

Now, I have no idea why you'd actually want to call constructors or destructors without an object, since they're really normally meant to be called with objects, but it's something interesting I thought I'd share. It turns out that constructors and destructors aren't really that special in C++. They're just regular functions like almost anything else and can be called that way. You just need to know how to do it.

I'll start with destructors, which may seem backwards, but doing this with destructors is actually easier than with constructors. It's not that much easier, but it makes a bit more sense. Let's start by calling a destructor with an object. We'll get to the no-object case next.

To call a destructor if you have an object, well, just call it:


struct A {
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
};

void main()
{
A a;
a.~a(); // just call it like anything else
}


So how do we do this without an object? We need to think about what happens when any member function is called. When the compiler compiles a member function call, all it does differently from a normal function call is to add a pointer to the object as an extra parameter (at the begining) and call the function normally. Now certain compilers even use a different calling convention, but in the end, it's just a modified function call.

So can we get the compiler to make that function call, but not require a real object to pass in? Of course:


A* a = NULL;
a->~a();


or more compactly:


((A*)NULL)->~a();


Now, as long as nothing in the destructor actually tries to access the this pointer (by accessing data members for example), then all is well. The destructor is called and no object was ever created.

What is this useful for? I have no idea, but one thought that I had centered around one particularly special behavior of destructors. They automatically call the destructor of the parent class. Every other type of member function doesn't do this. Parent class versions of functions much be explicitly called. In some cases, this can be frustrating, especially in template classes where the parent class may be hard to determine.

In order to call a constructor without creating an object, we can use a special feature of C++ rarely seen outside of things like STL and embedded code, placement new. Placement new is just like new, except that it doesn't allocate memory for you. It leaves the memory allocation up to the programmer. Here's what it looks like normally.


void* ptr = malloc(sizeof(A));
A* a = new (ptr) A();


The syntax is a little funny, but that's okay. All we're doing there is calling the constructor member function using our own pointer (ptr) for the this pointer instead an automatically generated one.

So, using the same trick of using NULL instead of a valid pointer as we did with the destructors, we can do the same with constructors:


A* a = new ((void*) NULL) A();


Except it doesn't work. At least with gcc. The constructor never is called since the this pointer is NULL. Well, no worries, we're not trying to actually do anything with the this pointer, so we just need to pass in something that the compiler thinks is good.


A* a = new ((void*) 1) A();


Let's get rid of the assignment, since we're not going to use that pointer anyway.


new ((void*) 1) A();


And voila, we're calling constructors. No object needed.

One key point here that should be repeated is that none of this will work on destructors or constructors that actually try to dereference the this pointer in any way. The most obvious time this is done is when accessing data members. Another is calling virtual functions, since the virtual function table will need to be referenced. Also, any class hierarchy that includes virtual inheritance won't work since that mechanism uses the this pointer as part of knowing what destructor to call.

Labels: ,

Comments:
This is a really bad idea. Destructors actually modify the vtable, so subsequent calls to the object will be very weird. For example:
#include

struct muh {
virtual ~muh() { }
virtual void m(void) { printf("Hello Bananas\n"); }
};

struct bar : public muh {
virtual ~bar() { }
virtual void m(void) { printf("Hello Monkeys\n"); }
};

struct foo : public bar {
virtual ~foo() { }
virtual void m(void) { printf("Hello World\n"); }
};

int main()
{
foo *f = new foo();
f->~foo();
f->m();

return 0;
}

When executed, will produce:
$ ./a.out
Hello Bananas

Probably not what you want.
 
This technique is all about calling a destructor without the existence of an object, so there isn't a vtable to modify in the first place.
 
you may call ctor with
((A *)NULL)->A::A();
 
@futurograph I don't what that standard says, but gcc doesn't allow that.
 
Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?