Calling the base
A derived type usually adds to what its base does rather than replacing it.
base reaches the base's version of the work: base( ... ) in a constructor
builds the base part, and base.Method( ) calls the base's method from
inside the derived one.
type Account extends Object
'' Defaults give Account a constructor that needs no argument, which
'' every type derived from it needs to be copyable.
declare constructor( byref owner as ustring = "", _
byval opening as int64 = 0 )
declare virtual function Describe( ) as ustring
owner as ustring
balance as int64
end type
constructor Account( byref owner as ustring, byval opening as int64 )
this.owner = owner
balance = opening
print " Account constructor"
end constructor
function Account.Describe( ) as ustring
return owner & ": " & balance
end function
type Savings extends Account
declare constructor( byref owner as ustring, byval opening as int64, _
byval rate as float64 )
declare function Describe( ) as ustring override
rate as float64
end type
constructor Savings( byref owner as ustring, byval opening as int64, _
byval rate as float64 )
'' base( ... ) picks the base constructor; it runs before the rest.
base( owner, opening )
this.rate = rate
print " Savings constructor"
end constructor
'' base.Describe( ) calls Account's version, not this one, so the
'' derived text extends the base text instead of repeating it.
function Savings.Describe( ) as ustring
return base.Describe( ) & " at " & rate & "%"
end function
dim as Savings s = Savings( "Ada", 500, 2.5 )
dim as Account ptr p = @s
print p->Describe( )
Output
Account constructor
Savings constructor
Ada: 500 at 2.5%
base( owner, opening ) chooses the base constructor. It is the first
statement of the Savings constructor, and the Account part is built
before anything else runs, as the output shows. Its arguments choose among
the base's constructors like any overloaded call. Without a base( ... )
line, the base's default constructor runs.
base.Describe( ) calls Account.Describe, even though Describe is
virtual and this is a Savings. A plain Describe( ) there would call
Savings.Describe again, forever. Building on the base's text means the two
descriptions cannot drift apart.
The comment on Account's constructor matters. A derived type is given a
copy constructor without writing one, and that free one starts by building
the base part with the base's default constructor. If the base has none,
the derived type cannot be copied, and the compiler says so at its
declaration:
'' A base with no constructor that takes no argument.
type Account
declare constructor( byval opening as int64 )
balance as int64
end type
constructor Account( byval opening as int64 )
balance = opening
end constructor
type Savings extends Account
declare constructor( byval opening as int64 )
rate as float64
end type
constructor Savings( byval opening as int64 )
base( opening )
end constructor
dim as Savings s = Savings( 100 )
print s.balance
nodefault.tiko(11,1): error TKO0926: 'savings' needs a copy constructor: its base has no constructor that takes no argument, so none can be made for it
|
11 | type Savings extends Account
| ^
There are two ways out: give the base constructor's parameters default
values, as base.tiko does, or write the copy constructor yourself,
declare constructor( byref other as const Savings ), and call base( ... )
from it.
base( ... ) belongs on the first line of the constructor, but the compiler
does not yet insist on it. Written later, it is accepted without a word, and
whatever the constructor assigned to its own fields before it is lost:
this.rate = rate followed by base( owner, opening ) leaves rate at 0.
Keep base( ... ) first.
Activities
- Add a
Checkingtype that extendsAccountwith anoverdraftfield, and give it aDescribethat builds onbase.Describe( ). - Fix
nodefault.tikothe second way, with a copy constructor of its own.