sendData
over the network, and we want it to be able to send data via TCP and UDP. One easy way to accomplish this is to create a parent class called Socket
with a virtual function called sendData
. Then we have two children classes called UDPSocket
and TCPSocket
, which override the sendData
function to send the data over the appropriate protocol.In the code that uses the socket, we create an object of type Socket
, and create whichever socket we are using in this instance. Each time we call the sendData
function, the sendData
function will be called from the proper subclass of Socket
, whether UDPSocket
or TCPSocket
, based on which type of Socket
object was originally created.
The biggest advantage here is that if a new protocol—QDP, for example—is invented, you simply create a new QDPSocket
class, and then change the line of code where the object is created. Then all calls to sendData
will call the new QDPSocket
version of sendData
without the need to change all the calls individually.
In the case of nonvirtual functions, the function to be executed is determined at compile time. If the object is an instance of the parent class, the parent class’s function will be called, even if the object at runtime belongs to the child class. When a virtual function is called on an object of the child class, the child class’s version of the function may be called, if the object is typed as an instance of the parent class.
shows a code snippet that will execute differently if the function is virtual or nonvirtual.
class A
. While the object at ❸ could be a subclass of class A
, at compile time, we know that it is an object of class A
, and the foo
function for class A
is called. This is why the code on the left will print “Class A.”In the case of virtual functions, the determination of which function to call is made at runtime. If a class A
object is called at runtime, then the class A
version of the function is called. If the object is of class B
, then the class B
function is called. This is why the code on the right will print “Class B.”
This functionality is often referred to as polymorphism. The biggest advantage to polymorphism is that it allows objects that perform different functionality to share a common interface.
g
function from the two code snippets in . On the left is the nonvirtual function call to foo
, and on the right is the virtual call.Non-virtual function call | Virtual function call |
---|---|
shows how the virtual function is used in to determine which code to call. The first 4 bytes of the object are a pointer to the vtable. The first 4-byte entry of the vtable is a pointer to the code for the first virtual function. , we see the first vtable entry being accessed. To find the code that is called, we must find the vtable in memory and then go to the first function in the list. Nonvirtual functions do not appear in a vtable because there is no need for them. The target for nonvirtual function calls is fixed at compile time. shows the vtable for a class with three virtual functions. When you see a vtable, only the first value in the table should have a cross-reference. The other elements of the table are accessed by their offset from the beginning of the table, and there are no accesses directly to items within the table.. A switch offset table would have offsets to locations that are not subroutines, labeled |
call
instruction. Virtual functions almost always appear this way, whereas nonvirtual functions are typically referenced via a call
instruction.Remember that child classes automatically include all functions from a parent class, unless they override it. In , sub_4010E0
at ❶ and ❷ is a function from the parent class that is also in the vtable for the child class, because it can also be called for the child class.
You can’t always differentiate a child class from a parent class, but if one vtable is larger than the other, it is the subclass. In this example, the vtable at offset 4020F0 is the parent class, and the vtable at offset 4020DC is the child class because its vtable is larger. (Remember that child classes always have the same functions as the parent class and may have additional functions.)