#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)