0% found this document useful (0 votes)
34 views

COMP2006 Lecture 10 Function Pointers Virtual Functions and Vtables

This lecture covered: 1. Function pointers, which allow functions to be stored in variables and passed as arguments. This enables callback functions and arrays of functions. 2. Virtual and non-virtual functions in C++. Virtual functions allow polymorphic behavior where the function called is based on the object's actual type rather than the variable's type. 3. How virtual functions are implemented using vtables, which are tables of function pointers associated with each class that determine which function to call at runtime based on the object's type.

Uploaded by

zombiten3
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
34 views

COMP2006 Lecture 10 Function Pointers Virtual Functions and Vtables

This lecture covered: 1. Function pointers, which allow functions to be stored in variables and passed as arguments. This enables callback functions and arrays of functions. 2. Virtual and non-virtual functions in C++. Virtual functions allow polymorphic behavior where the function called is based on the object's actual type rather than the variable's type. 3. How virtual functions are implemented using vtables, which are tables of function pointers associated with each class that determine which function to call at runtime based on the object's type.

Uploaded by

zombiten3
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 38

COMP2006

C++ Programming
Lecture 10

Dr Chao Chen

1
This lecture

• Function pointers

• Virtual and non-virtual functions

2
Function pointers

3
Process structure in memory
Stack
Data area that grows downwards towards the heap
LIFO data structure, for local variables and parameters

Heap
Data area that grows upwards towards stack
Specially allocated memory (malloc, free, …, probably new, delete)
Data and BSS (uninitialised data) segment
Read-only: Constants String literals
Read/write: Global variables Static local variables

Code (or text) segment


The program code
4
Function pointers
• Functions are stored in memory
– You can ask for the address of them
– You can store these in function pointers

• Used a lot in low-level programming


– Operating system calls often want function pointers

• Function pointers can be passed into functions


– E.g. the for_each function on containers

• And used for ‘callback functions’


– Allows something to call you back
– ‘Call this function when an event happens’
– Event driven programming is VERY common 5
Simplest functions… 1/2
void f1() { printf( "f1(); " ); } f1 and f2 are
void f2() { printf( "f2(); " ); } normal functions
int main() Note: Not function calls, but strings!
{
void (*g1)() = NULL;
void (*g2)() = NULL;

printf("Test 1: " );
g1 = &f1;
(*g1)();
g1(); // Short way

printf("\nTest 2: " );
g2 = &f2;
(*g2)();
g2(); // Short way 6
Simplest functions… 2/2
void f1() { printf( "f1(); " ); }
void f2() { printf( "f2(); " ); }

int main()
{
void (*g1)() = NULL; g1 and g2 are function pointers
void (*g2)() = NULL;
of type void function()
printf("Test 1: " ); Initialised to NULL
g1 = &f1;
(*g1)();
Make g1 point at f1()
g1(); // Short way
Note: The & is optional.
printf("\nTest 2: " );
g2 = &f2;
As for arrays, function
(*g2)(); name is a pointer to it
g2(); // Short way 7
Simplest functions… output
void f1() { printf( "f1(); " ); }
void f2() { printf( "f2(); " ); }

int main()
{
void (*g1)() = NULL;
void (*g2)() = NULL;

printf("Test 1: " );
g1 = &f1;
(*g1)();
g1(); // Short way Test 1: f1(); f1();

printf("\nTest 2: " );
g2 = &f2;
(*g2)(); Test 2: f2(); f2();
g2(); // Short way 8
Assignment of pointers
void f1() { printf( "f1(); " ); }
void f2() { printf( "f2(); " ); }

int main()
{
void (*g1)() = NULL;
void (*g2)() = NULL;

g1 = &f1;
g2 = &f2;

// Assignment of function pointers


printf("\nTest 3: " );
g2 = g1; Look here
(*g2)();
g2(); // Short way
9
Assignment : the output
void f1() { printf( "f1(); " ); }
void f2() { printf( "f2(); " ); }

int main()
{
void (*g1)() = NULL;
void (*g2)() = NULL;

g1 = &f1;
g2 = &f2;

// Assignment of function pointers


printf("\nTest 3: " );
g2 = g1;
(*g2)(); Test 3: f1(); f1();
g2(); // Short way
10
Returning values
int f3() { printf( "f3(); " ); return 3; }
int f4() { printf( "f4(); " ); return 4; }

int main() Look here


{
int (*g3)() = NULL;
int (*g4)() = NULL;

printf("\nTest 4: " );
g3 = &f3;
g4 = &f4;
printf( "Result = %d ", g3() );
printf( "Result = %d ", g4() ); 11
Returning values : output
int f3() { printf( "f3(); " ); return 3; }
int f4() { printf( "f4(); " ); return 4; }

int main()
{
int (*g3)() = NULL;
int (*g4)() = NULL;

Test 4:
printf("\nTest 4: " );
f3(); Result = 3
g3 = &f3; f4(); Result = 4
g4 = &f4;
printf( "Result = %d ", g3() );
printf( "Result = %d ", g4() ); 12
Passing parameters
int f3() { printf( "f3(); " ); return 3; }

int f5( int i )


{ printf( "f5( %d ); ", i ); return i+1; }

int f6( int i )


{ printf( "f6( %d ); ", i ); return i-2; }

int main()
{
int (*g3)() = &f3;
int (*g5)( int ) = &f5; Look here
int (*g6)( int ) = &f6;

printf("\nTest 5: " ); g5( 1 ); g6( 2 );


printf("\nTest 6: " ); g5( g3() );
printf("\nTest 7: " ); g6( g4() );
printf("\nTest 8: " ); g6( g5( g3() ) ); 13
Passing parameters : output
int f3() { printf( "f3(); " ); return 3; }

int f5( int i )


{ printf( "f5( %d ); ", i ); return i+1; }

int f6( int i )


{ printf( "f6( %d ); ", i ); return i-2; }

int main() Test 5: f5( 1 ); f6( 2 );


{ Test 6: f3(); f5( 3 );
int (*g3)() = &f3; Test 7: f4(); f6( 4 );
int (*g5)( int ) = &f5; Test 8: f3(); f5( 3 ); f6( 4 );
int (*g6)( int ) = &f6;

printf("\nTest 5: " ); g5( 1 ); g6( 2 );


printf("\nTest 6: " ); g5( g3() );
printf("\nTest 7: " ); g6( g4() );
printf("\nTest 8: " ); g6( g5( g3() ) ); 14
Callback function
int f5( int i ) int f6( int i )
{ {
printf("f5( %d ); ",i); printf("f6( %d ); ",i);
return i+1; return i-2;
} }
Look here
// Execute a callback int main()
function ten times {
int Do10( int (*g5)( int ) = &f5;
int (*func)(int) ) int (*g6)( int ) = &f6;
{
int i = 0, j = 0; printf("\nTest 9: " );
for ( ; i < 10 ; i++ ) Do10( g5 );
j = func( j ); printf("\nTest 10: " );
return j; Do10( g6 );
} …
15
Callback function : output
int f5( int i ) int f6( int i )
{ {
printf("f5( %d ); ",i); printf("f6( %d ); ",i);
return i+1; return i-2;
} }

// Execute a callback int main()


function ten times {
int Do10( int (*g5)( int ) = &f5;
int (*func)(int) ) int (*g6)( int ) = &f6;
{
int i = 0, j = 0; printf("\nTest 9: " );
for ( ; i < 10 ; i++ ) Do10( g5 );
j = func( j ); printf("\nTest 10: " );
return j; Do10( g6 );
} …
Test 9: f5( 0 ); f5( 1 ); f5( 2 ); f5( 3 ); f5( 164 );
f5( 5 ); f5( 6 ); f5( 7 ); f5( 8 ); f5( 9 );
Callback function
int f5( int i ) int f6( int i )
{ {
printf("f5( %d ); ",i); printf("f6( %d ); ",i);
return i+1; return i-2;
} }

// Execute a callback int main()


function ten times {
int Do10( int (*g5)( int ) = &f5;
int (*func)(int) ) int (*g6)( int ) = &f6;
{
int i = 0, j = 0; printf("\nTest 9: " );
for ( ; i < 10 ; i++ ) Do10( g5 );
j = func( j ); printf("\nTest 10: " );
return j; Do10( g6 );
} …
Test 10: f6( 0 ); f6( -2 ); f6( -4 ); f6( -6 ); f6( -8
17 );
f6( -10 ); f6( -12 ); f6( -14 ); f6( -16 ); f6( -18 );
Function pointer typedef
You can use typedef to create a new type which can
simplify the code readability considerably:

/* Make name fptr1 mean function pointer:


return int, int parameter */
typedef int (*fptr1)(int);

/* return void, float parameter */


typedef void (*fptr2)(float);

/* Usage: */
fptr1 f= …;
fptr2 g= … , myfptr = …; 18
Arrays of function pointers
#include <cstdio> int main()
{
int f1( int i ) int i,j,k;
{ return i; }
int f2( int i ) fptr fptrarray[6];
{ return i*2; } fptrarray[0] = &f1;
int f3( int i ) fptrarray[1] = &f2;
{ return i*3; } fptrarray[2] = &f3;

/* typedef makes the name i = fptrarray[2](2);


fptr mean a function
pointer: int func (int) */ j = fptrarray[0](i);
typedef int (*fptr)(int);
k = fptrarray[2](j);
1. Addresses of functions are printf( "%d %d %d\n",
stored in an array i, j, k );
2. Can call functions by index in return 0;
}
the array rather than by name 19
v-tables

20
Example: virtual functions
class BaseClass
{
public: char* foo() { return "BaseFoo"; }
virtual char* bar() { return "BaseBar"; }
};

class SubClass : public BaseClass


{
public: char* foo() { return "SubFoo"; }
virtual char* bar() { return "SubBar "; }
};

int main()
{
SubClass* pSub = new SubClass;
BaseClass* pSubAsBase = pSub;
printf( "pSubAsBase->foo() %s\n", pSubAsBase->foo() );
printf( "pSubAsBase->bar() %s\n", pSubAsBase->bar() );
delete pSub; 21
}
Virtual and non-virtual functions
• For normal/default (non-virtual) functions:
– Type of pointer determines function to call
– i.e. the apparent type of the object, from pointer type
– Use the type of the object the compiler thinks it is:
• Type of pointer (or reference) to the object
• Type of the member function making the call (hidden this ptr)
• Easier for the compiler, type is known at compile-time
• Virtual function:
– Finds out the actual function to call based upon the
object type AT RUNTIME - much more difficult - slower
– i.e. look-up table: ‘which function should I really call’
– Works in the same way as Java functions
22
Possible Implementation
• How could virtual functions be implemented?

• One possible implementation uses


vtables (virtual function tables) and
vpointers (pointers to a vtable)

• This is equivalent to having a hidden pointer:


struct EMPLOYEE
{
void** vpointer; Pointer to array of
char strName[64]; function pointers
int iEmployeeID; Hidden – you do not see it!
} Employee;
The vtable (virtual function table)
class BaseClass Class has a
Object has a hidden
{ public: pointer to the vtable vtable (which
virtual void foo1(); for its class (vpointer) functions to call)
virtual void foo2();
virtual void foo3(); BaseClass::foo1()
BaseClass
} BaseClass::foo2()
object
BaseClass::foo3()
class SubClass1 : public BaseClass
{ public: SubClass1::foo1()
virtual void foo1(); SubClass1 SubClass1::foo2()
virtual void foo2(); object BaseClass::foo3()
virtual void foo4(); SubClass1::foo4()
}
class SubClass2 : public SubClass1 SubClass2::foo1()
{ public: SubClass1::foo2()
SubClass2
virtual void foo1(); object SubClass2::foo3()
virtual void foo3(); SubClass1::foo4()
virtual void foo5(); SubClass2::foo5()
24
}
Example (1/8)
class Base
{
public:
virtual void f1();
virtual void f2();
virtual void f3();
void f4();
}

25
Example (2/8)
class Base Base:
{ f1()
public: f2()
virtual void f1(); f3()
virtual void f2();
virtual void f3();
void f4();
}

26
Example (3/8)
class Sub1 : public Base Base:
{ f1()
public: f2()
f3()
virtual void f2();
void f5(); Sub1:
virtual void f6(); ?
}

27
Example (4/8)
class Sub1 : public Base Base:
{ f1()
public: f2()
f3()
virtual void f2();
void f5(); Sub1:
virtual void f6(); f1()
} f2()
f3()
f6()
28
Example (5/8)
class Sub2 : public Sub1 Base:
{ f1()
public: f2()
virtual void f1(); f3()
virtual void f2();
} Sub1:
Sub2: f1()
? f2()
f3()
f6()
29
Example (6/8)
class Sub2 : public Sub1 Base:
{ f1()
public: f2()
virtual void f1(); f3()
virtual void f2();
} Sub1:
Sub2: f1()
f1() f2()
f2() f3()
f3() f6()
f6() 30
Example (7/8)
class Sub3 : public Sub2 Base:
{ f1()
public: f2()
virtual void f7(); f3()
virtual void f8();
void f9(); Sub1:
} Sub2: f1()
Sub3:
f1() f2()
?
f2() f3()
f3() f6()
f6() 31
Example (8/8)
class Sub3 : public Sub2 Base:
{ f1()
public: f2()
virtual void f7(); f3()
virtual void f8();
Sub3:
f1() void f9(); Sub1:
}
f2() Sub2: f1()
f3() f1() f2()
f6() f2() f3()
f7() f3() f6()
f8() f6() 32
Final comments and special
cases

33
Virtualness is inherited in C++
• virtual-ness is inherited
– If a function is virtual in the base class then
the function is virtual in the derived class(es)
• Including destructor! (If virtual in base class then
destructors of derived classes are virtual)
– Even when the keyword virtual is not used in
the derived class
• All functions in Java are virtual by default
– Unless you make them ‘final’
– Java, final functions are different: they cannot
be re-implemented in sub-classes
34
sizeof()
• Member Functions act on objects of the class type
– Even member functions
• Member Functions are not actually in the objects
– The functions just have a hidden ‘this’ parameter saying which
object they apply to
– The hidden ‘this’ parameter and permission to access private
members are the ONLY things special about member functions
• The object is the collection of data
– But also includes any hidden data and pointers that the
compiler adds to implement features (e.g. vtable)
• Adding a member function to an existing class will not
usually make the objects bigger
• Exception: adding the first virtual function may add a
vtable pointer (or equivalent)
35
– Understand why this is the case!
Should a function be virtual?
• If member function is called from a base class
function or through a base class pointer
AND the behaviour should depend on class type
of the object
then the member function has to be virtual
– Otherwise when it is called by the base class, or
through a base-class pointer, the base-class version
will be called, not your modified version
• Utility functions will often not be virtual
– When functionality is not expected to be changed in
sub-classes
– Faster to call these functions – no look-up needed
– Makes it easier for function to be inline, which can
make code even faster: no function call needed 36
What to know
• Some equivalent of a vpointer exists in
objects with virtual functions
– Just one pointer is needed in each object
• Only virtual functions appear in vtables
– No need to record non-virtual functions
• Looking up which function to call is slower than
calling a non-virtual function
1. Go to the object itself
2. Go to the vtable (following the vpointer)
3. Look up which function to call
4. Call the function

37
Next lecture
• Automatically created methods:
– Default Constructor
– Copy Constructor
– Assignment operator
– Destructor
• Conversion constructors
• Conversion operators
• Friends

38

You might also like