Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Slicing

A derived object can be passed wherever its base type is wanted. How it is passed decides whether the procedure gets the object or only part of it.

type Animal extends Object
    declare virtual function Sound( ) as ustring
    legs as int32
end type

function Animal.Sound( ) as ustring
    return "..."
end function

type Dog extends Animal
    declare function Sound( ) as ustring override
    tricks as int32
end type

function Dog.Sound( ) as ustring
    return "woof"
end function

sub ByReference( byref a as Animal )
    print "byref:  "; a.Sound( ); ", a Dog: "; cbool( a is Dog )
end sub

'' byval makes a new Animal from the Animal part of the argument.
sub ByValue( byval a as Animal )
    print "byval:  "; a.Sound( ); ", a Dog: "; cbool( a is Dog )
end sub

dim as Dog rex
rex.tricks = 3
ByReference( rex )
ByValue( rex )

'' Assignment to an Animal does the same.
dim as Animal pet = rex
print "copied: "; pet.Sound( ); ", a Dog: "; cbool( pet is Dog )

'' A pointer keeps the whole object.
dim as Animal ptr p = @rex
print "ptr:    "; p->Sound( ); ", a Dog: "; cbool( *p is Dog )

'' The hidden table pointer is the whole of an Object.
print "sizes:"; sizeof( Object ); sizeof( Animal ); sizeof( Dog )

Output

byref:  woof, a Dog: true
byval:  ..., a Dog: false
copied: ..., a Dog: false
ptr:    woof, a Dog: true
sizes: 8 16 24

ByReference receives rex itself, so the virtual call reaches Dog.Sound and is Dog is true.

ByValue receives a new Animal, built by copying the Animal part of rex. That copy is an Animal through and through: it says ..., it is not a Dog, and tricks is not part of it at all. The derived part has been sliced off. Assigning a Dog to an Animal variable does the same. A pointer, like a byref parameter, refers to the whole object.

The last line shows the hidden field behind all of this. Object is 8 bytes: the pointer to the type's table of virtual methods, and nothing else. Animal adds legs and rounds up to 16, and Dog adds tricks and rounds up to 24.

Pitfall

Nothing warns about slicing, because from the compiler's side nothing is wrong: an Animal was asked for and an Animal was made. Pass objects of a type with virtual methods byref or by pointer, never byval, and keep them in Animal ptr variables rather than Animal ones.

Activities

  1. Change ByValue to take its parameter byref and compare the output.
  2. Put rex in a dim as Animal pets( 0 to 0 ) array and call Sound on the element. Then try an array of Animal ptr.