Virtual destructors
An object created with new and deleted through a pointer to its base type
is destroyed by whichever destructor that pointer's type names, unless the
destructor is virtual. When the derived type is the one holding the
resource, that is the difference between closing it and leaking it.
'' Connection has a virtual destructor; Plain does not.
type Plain
declare destructor( )
id as int32
end type
destructor Plain( )
print " ~Plain"
end destructor
type PlainSocket extends Plain
declare destructor( )
end type
destructor PlainSocket( )
print " ~PlainSocket: socket closed"
end destructor
type Connection extends Object
declare virtual destructor( )
end type
destructor Connection( )
print " ~Connection"
end destructor
type Socket extends Connection
declare virtual destructor( ) '' virtual at this level too
end type
destructor Socket( )
print " ~Socket: socket closed"
end destructor
type TlsSocket extends Socket
declare destructor( )
end type
destructor TlsSocket( )
print " ~TlsSocket: session ended"
end destructor
print "delete through a Plain ptr:"
dim as Plain ptr p = new PlainSocket
delete p '' only ~Plain runs
print "delete through a Connection ptr:"
dim as Connection ptr c = new TlsSocket
delete c '' the whole chain runs
Output
delete through a Plain ptr:
~Plain
delete through a Connection ptr:
~TlsSocket: session ended
~Socket: socket closed
~Connection
delete p ran ~Plain and nothing else. PlainSocket's destructor, the one
that closes the socket, never ran, and nothing reported it: the program
compiled and ran cleanly.
Connection declares virtual destructor( ), so delete c looks the
destructor up in the object's table. It finds TlsSocket's, runs it, and
then runs each base's in turn, from the most derived to Connection itself.
virtual on a destructor is not inherited by a level that does not say
it. Socket repeats declare virtual destructor( ). Declared as a plain
destructor( ) there instead, delete c runs only ~Socket and
~Connection, and ~TlsSocket is skipped. Leaving Socket with no
destructor at all is no better: the one the compiler writes for it is not
virtual either, and only ~Connection runs. Every level that may be deleted
through a base pointer declares virtual destructor( ), even an empty one.
Objects in a scope, or passed byref, are destroyed by their own type and
are not affected: the rule matters only for delete through a pointer to a
base. new and delete themselves are covered in
Memory and Resources.
Activities
- Change
Socket's destructor to a plaindeclare destructor( )and check the output against the pitfall above. - Make
Plain's destructor virtual. What else has to change for that to compile?