Animal awhat the compiler seesPreparing questions and your practice workspace.
Preparing questions and your practice workspace.
Chapter 2 · Dispatch & abstraction
Trace calls from handle and object types, distinguish overriding from hiding, and use abstract classes and checked casts safely.

The language bench / dispatch
class Animal;
virtual function string speak(); return "Animal"; endfunction
endclass
class Dog extends Animal;
function string speak(); return "Dog"; endfunction
endclass
Animal a; Dog d = new; a = d;
a.speak();Question directory
Open an authored answer, then use the adjacent object diagram or code example to verify the mechanism.
Trace virtual calls, non-virtual hiding, super calls, static method resolution, and the constructor-time dispatch trap without guessing from the variable name.
Animal awhat the compiler seesDogwhat was allocatedDog::speak()woofRuntime dispatchThe base declaration marked the method virtual, so the object's dynamic type selects the implementation.
Virtual calls follow the dynamic object type. Non-virtual and static calls are bound from the declared type or class scope, even when the handle refers to a derived object.
When the base declaration is virtual, a call through any compatible handle dispatches from the dynamic type of the object. A base handle referring to a Dog therefore invokes Dog::speak().
class Animal;
virtual function string speak();
return "noise";
endfunction
endclass
class Dog extends Animal;
function string speak();
return "woof";
endfunction
endclass
Dog dog = new;
Animal animal = dog;
$display("%s", animal.speak()); // woofA derived method overrides a matching virtual base method and participates in runtime dispatch. A same-named method whose base declaration is non-virtual only hides that name; calls remain bound by the handle's declared type.
Derived::who even when the method is non-virtual because d is declared Derived.Base::who when the base declaration is non-virtual.| Base declaration | Call through Base handle | Call through Derived handle |
|---|---|---|
| virtual | Derived override | Derived override |
| non-virtual | Base method | Derived hidden method |
super.method() invokes the immediate superclass implementation directly from the derived method. It is useful for extending base behavior rather than replacing it completely.
C::vf, super.vf() names B::vf when C extends B.B::vf also calls super.vf(), that nested call reaches A::vf.class A;
virtual function string name(); return "A"; endfunction
endclass
class B extends A;
function string name();
return {"B(", super.name(), ")"};
endfunction
endclass
class C extends B;
function string name();
return {"C(", super.name(), ")"};
endfunction
endclass
// C object prints C(B(A))Static members belong to a class type, not an object. A derived same-named static member shadows a separate base member, and a call is resolved from class scope or the handle's declared type rather than the dynamic object type.
Base::inc() updates Base::cnt and Der::inc() updates Der::cnt.Base::get() is 3, Der::get() is 120, and hb.get() resolves to the base value 3.class Base;
static int cnt = 0;
static function void inc(); cnt++; endfunction
static function int get(); return cnt; endfunction
endclass
class Der extends Base;
static int cnt = 100;
static function void inc(); cnt += 10; endfunction
static function int get(); return cnt; endfunction
endclass
Base b = new;
Der d = new;
Base hb = d;
Base::inc(); Der::inc();
b.inc(); d.inc(); hb.inc();
// Base::get() = 3, Der::get() = 120, hb.get() = 3Virtual dispatch can enter the derived override while the base constructor is still running, before the derived constructor has initialized its fields. The override observes default or partially initialized derived state.
build() and the object is DerB, DerB::build can run before DerB.new assigns ready = 1.class DerB extends BaseB;
int ready;
function new();
super.new();
ready = 1;
endfunction
function void build();
$display("ready=%0d", ready);
endfunction
endclass
DerB d = new;
d.build(); // safe: construction is completeFor every call, write down the handle's declared type, the object's dynamic type, and whether the base declaration is virtual. Virtual calls follow the dynamic type; non-virtual calls follow the handle type; super calls follow the immediate base implementation.
C::vf when vf is virtual but A::nf when nf is non-virtual.$cast to B, the B handle still refers to the same C object; a non-virtual nf call now binds to B::nf.C::nf, while every compatible handle still dispatches virtual vf to C.| Call kind | Lookup source | Object copied? |
|---|---|---|
| virtual instance method | Dynamic object type | No |
| non-virtual instance method | Declared handle type | No |
| static method | Class scope or handle type | No |
| super.method() | Immediate superclass implementation | No |
Use virtual classes and pure virtual methods to define incomplete base types, then move through the hierarchy with safe upcasts and checked downcasts.
virtual class Shapepure virtual function area()Rectarea() implementedCirclearea() implementedRect handle→Shape handleupcast · always safeShape handle→Rect handle$cast() · runtime checkA concrete child must satisfy every inherited pure virtual contract. Upcasting preserves the object behind a base handle; $cast() verifies a downcast at runtime.
A virtual class is an abstract class type that cannot be instantiated directly. A virtual method is an instance method that supports runtime overriding; a concrete class may contain virtual methods and still be instantiable.
virtual class may contain implemented properties and ordinary or virtual methods.| Construct | Main effect | Can enclosing class be instantiated? |
|---|---|---|
virtual class | Type is abstract | No |
| virtual method | Call dispatches dynamically | Yes, if class is concrete |
| pure virtual method | Derived concrete type must implement it | Only legal in an abstract class |
A pure virtual method declares a signature without an implementation. Every non-virtual derived class must provide a matching implementation or the derived class must remain virtual.
format(), send_pkt(), or area().virtual class Shape;
pure virtual function int area();
endclass
class Rect extends Shape;
int w, h;
function new(int w = 1, int h = 1);
this.w = w;
this.h = h;
endfunction
function int area();
return w * h;
endfunction
endclassEvery derived object satisfies its base type, so assigning a derived handle to a base handle is safe. A base handle may refer to many possible dynamic types, so $cast() must verify a downcast at runtime.
$cast(destination, source) returns 1 and assigns the handle when the dynamic object is compatible.Rect original = new(3, 4);
Shape base = original; // safe upcast
Rect recovered;
if ($cast(recovered, base))
$display("area=%0d", recovered.area());
else
$display("incompatible dynamic type");The derived method must preserve the base method's name and compatible prototype, including return type and formal argument contract. It then remains virtual even if the keyword is omitted in the derived declaration.
Yes. A virtual class cannot be constructed directly, but its handle may refer to any compatible concrete derived object and invoke the abstract contract polymorphically.
Rect::area and returns 12.