Thursday, March 13, 2008

vtable and __declspec(novtable) (aka ATL_NO_VTABLE)

__declspec(novtable) is a storage-class attribute and Microsoft specific applied to classes declaration.
It's purpose is to tell the compiler to not add the default vtable initialization\deinitialisation code in the class ctor\destructor.
When the compiler sees a class with virtual methods, it automatically adds vtable init\deinit code in the class ctor\destructor methods. Remember that the vtable is pointer to an array of pointers of the class virtual methods. I guess the initializatio is needed at the ctor time because at the time when your ctor method code is entered, the inner workings of the C++ object need to be initialized. Note that although the vtable is available on thee ctor code, its useless to use it, i.e. calling any virtual method. This is because the vtable of any derived class is not yet initialized.

So, using __declspec(novtable) we will not have the vtable initialized\deinitialized in the ctor\destructor methods.
This means that we cannot use the vtable in any way. More specifically, it means we cannot call any of the object's virtual method.

This has interesting effects. The obvious use is for interfaces, classes with pure virtual functions.

class __declspec(novtable) IFoo
{
public:
virtual ~IFoo()
{
}

virtual void foo() = 0;
};

class FooImpl : public IFoo
{
public:
virtual void foo();
};

IFoo* p = new FooImpl();
p->foo();

So the ctor\dctor of IFoo doesn't have any vtable initialization code as FooImpl has.
When we call foo() on the object the compiler sees that we use a pointer to an object and the method is virtual. Hence, it will use the vtable to get to the method. Vtable is a hidden pointer data in a class. It is always the first data when looking at the raw object in memory.
The FooImpl ctor initialized the vtable so the call is legit and always succeeds.
Remember that although the we call the Foo method of a pointer to IFoo, the vtable hidden data points to the FooImpl.
The vtable pointer is always the same in the raw object in all class derivations.

Another legit code is the following one:

class __declspec(novtable) A {
virtual void func1() { cout<<1; }
virtual void func2() { cout<<2; }
virtual void func3() { cout<<3; }
};

class A1 : public A {
// no func1, func2, or func3
};

It is OK to write:
A* p = new A1();
p->func1();
p->func2();
p->func3();

because A1 ctor initialize the object vtable.

And its NOT OK to write:
A* p = new A();
A->func1(); // undefined behaviour
p->func2(); // undefined behaviour
p->func3(); // undefined behaviour

It is OK to write:
A p;
p.func1();
p.func2();
p.func3();

because calling methods not using a pointer does not make use of vtable.

When using the novtable specifier, always think if the vtable is needed.
For example, you cannot use novtable for a class calling virtual methods in ctor\destructor. This is because the novtable told the compiler to omit the code which initialize the vtable before entering the ctor code.

This is very well described in the paper:
http://msdn.microsoft.com/msdnmag/issues/0300/c/

No comments: