A set of classes can be created that can be viewed as all being derived from a common base class. For example, a common base class of type BasePacket that sets out the structure of packets but is incomplete would never be instantiated. From this base class, however, a number of useful subclasses could be derived, such as Ethernet packets, token ring packets, GPSS packets, and satellite packets. Each of these packets might look very similar, all needing the same set of methods, but they could vary significantly in terms of their internal details.
A base class sets out the prototype for the subclasses. Because the base class is not intended to be instantiated, it can be made abstract by specifying the class to be virtual:
virtual class BasePacket;
Abstract classes can also have virtual methods. Virtual methods are a basic polymorphic construct. A virtual method overrides a method in all the base classes, whereas a normal method only overrides a method in that class and its descendants. One way to view this is that there is only one implementation of a virtual method per class hierarchy, and it is always the one in the latest derived class. Virtual methods provide prototypes for subroutines, i.e., all of the information generally found on the first line of a method declaration: the encapsulation criteria, the type and number of arguments, and the return type if it is needed. Later, when subclasses override virtual methods, they must follow the prototype exactly. Thus, all versions of the virtual
method look identical in all subclasses:
virtual class BasePacket;
virtual function integer send(bit[31:0] data);
endfunction
endclass
class EtherPacket extends BasePacket;
function integer send(bit[31:0] data);
// body of the function
…
endfunction
endclass
EtherPacket is now a class that can be instantiated. In general, if an abstract class has any virtual methods, all of the methods must be overridden (and provided with a method body) for the subclass to be instantiated. If any virtual methods have no implementation, the subclass needs to be abstract.
An abstract class can contain methods for which there is only a prototype and no implementation (i.e., an incomplete class). An abstract class cannot be instantiated; it can only be derived. Methods of normal classes can also be declared virtual. In this case, the method must have a body. If the method does have a body, then the class can be instantiated, as can its subclasses.
Polymorphism: dynamic method lookup
Polymorphism allows the use of a variable in the superclass to hold subclass objects and to reference the methods of those subclasses directly from the superclass variable. As an example, assume the base class for the Packet objects, BasePacket, defines, as virtual functions, all of the public methods that are to be generally used by its subclasses. Such methods include send, receive, and print. Even though BasePacket is abstract, it can still be used to declare a variable:
BasePacket packets[100];
Now, instances of various packet objects can be created and put into the array:
EtherPacket ep = new; // extends BasePacket
TokenPacket tp = new; // extends BasePacket
GPSSPacket gp = new; // extends EtherPacket
packets[0] = ep;
packets[1] = tp;
packets[2] = gp;
If the data types were, for example, integers, bits, and strings, all of these types could not be stored into a single array, but with polymorphism, it can be done. In this example, because the methods were declared as virtual, the appropriate subclass methods can be accessed from the superclass variable, even though the compiler did not know—at compile time—what was going to be loaded into it.
For example, packets[1]
packets[1].send();
shall invoke the send method associated with the TokenPacket class. At run time, the system correctly binds the method from the appropriate class. This is a typical example of polymorphism at work, providing capabilities that are far more powerful than what is found in a nonobject-oriented framework.