Figure 1

foo1.cpp

  
#include <iostream.h>

//////////////////
// Typical class with constructor, destructor and three virtual
// functions.
//
class A {
   int x;
public:
   A();
   ~A();
   virtual void func1() { cout<<1; }
   virtual void func2() { cout<<2; }
   virtual void func3() { cout<<3; }
};

A::A()
{
   x=0;
}

A::~A()
{
}

void main()
{
   // Note: Need to use ptr to A (pa) since if you write
   // a.func1() the compiler is too smart and generates
   // a direct (static) fn call.
   A a;
   A* pa = &a;
   pa->func1();
   pa->func2();
   pa->func3();
}

Figure 2

foo2.asm

  
#;;; Edited assembly listing for foo2.cpp, generated with the
;;; compiler switch -FAs. Interesting stuff marked with *** -PD
;;;

   TITLE foo2.cpp
 ; lots of incomprehensible assembly code
•••
PUBLIC   ??0A@@QAE@XZ               ; A::A
PUBLIC   ?func1@A@@UAEXXZ           ; A::func1
PUBLIC   ?func2@A@@UAEXXZ           ; A::func2
PUBLIC   ?func3@A@@UAEXXZ           ; A::func3
PUBLIC   ??_7A@@6B@                 ; A::`vftable'
•••
;;; *** Here's the vtable for A
;;;
CONST SEGMENT
??_7A@@6B@
   DD FLAT:?func1@A@@UAEXXZ
   DD FLAT:?func2@A@@UAEXXZ
   DD FLAT:?func3@A@@UAEXXZ
CONST ENDS

;;; Following is the code for the constructor A:A.
;;; Note how vtable initialized (*** below).
;;;
_TEXT SEGMENT
_this$ = -4
??0A@@QAE@XZ PROC NEAR              ; A::A

; 15   : {

   push  ebp
   mov   ebp, esp
   push  ecx
   mov   DWORD PTR _this$[ebp], ecx
   mov   eax, DWORD PTR _this$[ebp]
   ;;; *** next line is where the vtable is initialized ***
   mov   DWORD PTR [eax], OFFSET FLAT:??_7A@@6B@
; A::'vftable'

; 16   :    x=0;

   mov   ecx, DWORD PTR _this$[ebp]
   mov   DWORD PTR [ecx+4], 0

; 17   : }

   mov   eax, DWORD PTR _this$[ebp]
   mov   esp, ebp
   pop   ebp
   ret   0
??0A@@QAE@XZ ENDP             ; A::A
_TEXT ENDS

;;; Following is the code for the destructor A:~A.
;;; Again the vtable is initialized (*** below).
;;;
PUBLIC   ??1A@@QAE@XZ               ; A::~A
_TEXT SEGMENT
_this$ = -4
??1A@@QAE@XZ PROC NEAR              ; A::~A

; 20   : {

   push  ebp
   mov   ebp, esp
   push  ecx
   mov   DWORD PTR _this$[ebp], ecx
   mov   eax, DWORD PTR _this$[ebp]
   ;;; *** next line is where the vtable is initialized ***
   mov   DWORD PTR [eax], OFFSET FLAT:??_7A@@6B@
; A::'vftable'

; 21   : }

   mov   esp, ebp
   pop   ebp
   ret   0
??1A@@QAE@XZ ENDP             ; A::~A

Figure 3

foo2.cpp

  
#include <iostream.h>

////////////////
// foo2: now I've added a derived class, A1

class A {
protected:
   int x;
public:
   A();
   ~A();
   virtual void func1() { cout<<1; }
   virtual void func2() { cout<<2; }
   virtual void func3() { cout<<3; }
};

//////////////////
// Now I've added a derived class with constructor, destructor
// and three virtual function overrides.
//
class A1 : public A {
public:
   A1();
   ~A1();
   virtual void func1() { cout<<4; }
   virtual void func2() { cout<<5; }
   virtual void func3() { cout<<6; }
};

 // same as foo1.cpp
•••

Figure 4

foo3.cpp

  
#include <iostream.h>

////////////////
// foo3: Now A is abstract, but compiler still generates vtable
// and initialization code. To see, compile with -FAs

//////////////////
// Now A's functions are PURE virtual functions, and A is
// abstract.
//
class A {
protected:
   int x;
public:
   A();
   ~A();
   virtual void func1()=0; // base class fns are pure
   virtual void func2()=0;
   virtual void func3()=0;
};

class A1 : public A {
public:
   A1();
   ~A1();
   virtual void func1() { cout<<4; }
   virtual void func2() { cout<<5; }
   virtual void func3() { cout<<6; }
};

 // same as foo2.cpp
•••

Figure 5

foo4.cpp

  
#include <iostream.h>

////////////////
// foo4: Now A uses __declspec(novtable). The compiler does not
// generate a vtable for A, nor vtable initialization code in
// constructor/destructor.
//
class __declspec(novtable) A {
protected:
   int x;
public:
   A();
   ~A();
   virtual void func1()=0; // base class fns are pure
   virtual void func2()=0;
   virtual void func3()=0;
};

class A1 : public A {
public:
   A1();
   ~A1();
   virtual void func1() { cout<<4; }
   virtual void func2() { cout<<5; }
   virtual void func3() { cout<<6; }
};

 // same as foo3.cpp
•••

Figure 6

foo5.cpp

  
#include <iostream.h>

//////////////////
// foo5: Now A implements the virtual functions, and is
// concrete--but I can still use __declspec(novtable)
// because instances of A are never created and A's
// constructor/destructor don't call any virtual fns.
//
class __declspec(novtable) A {
protected:
   int x;
public:
   A();
   ~A();
   virtual void func1() { cout<<1; }
   virtual void func2() { cout<<2; }
   virtual void func3() { cout<<3; }
};

//////////////////
// A1 inherits all virtual fns from A.
//
class A1 : public A {
public:
   A1();
   ~A1();
};

 // same as foo4.cpp
•••

Figure 7

foo5.asm

  
;;; Edited assembly listing for foo5.cpp, generated with the
;;; compiler switch -FAs. Interesting stuff marked with *** -PD
;;;

   TITLE foo5.cpp
 ; lots of incomprehensible assembly code

•••

;;; *** Here are A's virtual functions declared. Name mangling ;;; sure is fun!
PUBLIC   ?func1@A@@UAEXXZ           ; A::func1
PUBLIC   ?func2@A@@UAEXXZ           ; A::func2
PUBLIC   ?func3@A@@UAEXXZ           ; A::func3

;;; *** Here's A1's vtable declared.
PUBLIC   ??_7A1@@6B@             ; A1::'vftable'

;;; *** Here's A1's vtable defined: note that it points to A's
;;; functions. This is how the compiler implements inheritance!
;;;
CONST SEGMENT
??_7A1@@6B@ DD FLAT:?func1@A@@UAEXXZ         ; A1::'vftable'
   DD FLAT:?func2@A@@UAEXXZ
   DD FLAT:?func3@A@@UAEXXZ
CONST ENDS
_TEXT SEGMENT

;;; The rest is the same as before: A has no initialization
;;; code at all because it's declared with
;;; __declspec(novtable); whereas A1 has initialization
;;; code in its constructor and destructor to initalize
;;; the vtable to ??_7A1@@6B@ (vtable for A1)