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

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.

Pitfall

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

  1. Change Socket's destructor to a plain declare destructor( ) and check the output against the pitfall above.
  2. Make Plain's destructor virtual. What else has to change for that to compile?