Abstract/Virtual destructor/method behaviour

For other topics related to the FreeBASIC project or its community.
fxm
Posts: 9014
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Abstract/Virtual destructor/method behaviour

Postby fxm » Nov 10, 2012 17:48

We can test it with the new build of MOD (fbc 0.25) at http://www.freebasic.net/forum/viewtopic.php?p=181009#p181009

The first test result:
    dkl, good work.
    But IMHO, I think that the specifier "Abstract" should be forbidden for a destructor, otherwise the object will not destroyed!

The about inheritance bugs identified relative to the official revision 0.24.0 (now fixed or not), inducing some workarounds to implement when possible:

    Code: Select all

    '#859 Incompatibility between Delete[] (or only overload Delete) and polymorphism OPEN   2017-07-09
    '#833 Runtime error when calling a virtual procedure of a specific Type structure OPEN   2016-08-15
    '#789 bad codegen for call to virtual operator cast() as string (see also #725)   OPEN   2015-12-08
    '#787 Operator Let() (Assignment) incompatible with inheritance                   OPEN   2015-12-07
    '#764 For member operator, UDT without operator declaration breaks polymorphism   OPEN   2014-12-31
    '#732 Disallowing CONST on dtor, the virtuals dtors do not work on CONST objects  closed 2014-06-13
    '#725 Accessing virtual/static member on function return, it's called twice/never OPEN   2014-03-30
    '#725 New title: vtable lookups don't work-around sidefx, static method calls
    '     don't preserve sidefx (see also #789)
    '#711 Derived type does not inherit any non-static member operators from its base OPEN   2013-11-06
    '#693 How to call an overridden base member operator, in disabling polymorphism?  OPEN   2013-07-27
    '#672 Forward declaration considered as different type in signature of procedure  OPEN   2013-05-07
    '#671 GCC warning when passing by value a compatible pointer type                 closed 2013-07-20
    '#667 Byref (function results) not taken into account in signature for overriding closed 2013-03-27
    '#663 Base.method() must not enable polymorphism (only inheritance)               closed 2013-07-27
    '#645 Access to global duplicated symbol is captured by local symbol              OPEN   2013-01-05
    '#626 Overloaded operator New/Delete always has public access                     closed 2012-10-25
    '#619 GCC does not support syntax as 'Delete Cast(...)'                           closed 2013-04-12
    '#614 Operator = (assignment to a derived object) modifies RTTI                   closed 2014-05-04
    '#613 SWAP with base-type and derived-type doesn't give an error                  closed 2012-09-23
    '#611 Shared dynamic ptr array of forward derived-UDT alias => Bug                closed 2012-09-23
    '#609 Crazy derived types fields initialization by temporary type                 closed 2012-10-25
    '#605 GCC warning about incompatible Parent/Child pointer types                   closed 2013-04-12
    '#581 Locals break/override THIS access to inherited members (see also #645)   DUPLICATE 2014-05-08
    | #859 | #833 | #789 | #787 | #764 | #732 | #725 | #711 | #693 | #672 | #671 | #667 | #663 | #645 | #626 | #619 | #614 | #613 | #611 | #609 | #605 | #581 |

[Edit]:
- title
- link to new builds
- added table of remaining bugs about inheritance/virtuality/polymorphism
Last edited by fxm on Jul 14, 2017 19:55, edited 10 times in total.
MOD
Posts: 554
Joined: Jun 11, 2009 20:15

Re: Where get a Recent-Git-Build of FreeBASIC?

Postby MOD » Nov 10, 2012 17:50

See todo
fxm
Posts: 9014
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Abstract/Virtual destructor/method behaviour

Postby fxm » Nov 10, 2012 18:58

Yes I saw:
- classes with ABSTRACT destructor shouldn't be allowed to be DIM'ed or DELETE'd, since the implicit dtor call will just be a NULL pointer deref.

IMHO, this is not sufficient.
If we construct a Child instance, and if the Parent has an abstract destructor, the base part of the Child instance will not be destroyed (or program crash due to the null pointer which is called after the Child destructor):

Code: Select all

Type Parent Extends Object
  Declare Abstract Destructor ()
End Type

Type Child Extends Parent
End Type


Dim As Child Ptr c = New Child
Delete c
Sleep

Code: Select all

Aborting due to runtime error 12 ("segmentation violation" signal) in d:\Documen
ts and Settings\t0003830\Mes documents\FBIde0.4.6r4_fbc0.25.0\FBIDETEMP.bas::()

Appuyez sur une touche pour continuer...

IMHO 'Declare Abstract Destructor ()' must always be forbidden by compiler.
Last edited by fxm on Apr 12, 2015 9:20, edited 1 time in total.
counting_pine
Site Admin
Posts: 6169
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: Where get a Recent-Git-Build of FreeBASIC?

Postby counting_pine » Nov 10, 2012 20:10

In C++ a pure virtual destructor needs to still be defined. Presumably because the idea isn't to prevent subclasses being destroyed, but to require them to implement an explicit destructor. (If you wanted to prevent subclasses being destroyed you would make the destructor private rather than abstract.)

If 'delete p' appears in code, where p is a pointer to a class with an abstract destructor, it should probably proceed as normal anyway, on the assumption that it's actually pointing to a subclass which has a destructor implemented.
On that basis, DELETE shouldn't be disallowed, but maybe NEW should.
fxm
Posts: 9014
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Abstract/Virtual destructor/method behaviour

Postby fxm » Nov 10, 2012 20:41

counting_pine wrote:In C++ a pure virtual destructor needs to still be defined. Presumably because the idea isn't to prevent subclasses being destroyed, but to require them to implement an explicit destructor.

As that?

Code: Select all

Type Parent Extends Object
  Dim As String S = Space(100 * 1024 * 1024)
  Dim As Byte B(1 To 50 * 1024 * 1024)
  Declare Abstract Destructor ()
End Type

Type Child Extends Parent
  Declare Destructor ()
End Type

Destructor Child ()
  Base.S = "" 'mandatory to destroyed the inherited string character data
End Destructor


Print "free memory ="; Fre / 1024 / 1024; " MB"
Print
Print "'Dim As Child Ptr c = New Child'"
Dim As Child Ptr c = New Child
Print "free memory ="; Fre / 1024 / 1024; " MB"
Print
Print "'Delete c'"
Delete c
Print "free memory ="; Fre / 1024 / 1024; " MB"
Sleep

Code: Select all

free memory = 1054.52734375 MB

'Dim As Child Ptr c = New Child'
free memory = 904.07421875 MB

'Delete c'
free memory = 1054.375 MB

What's the interest of this configuration?
Last edited by fxm on Apr 12, 2015 9:21, edited 1 time in total.
counting_pine
Site Admin
Posts: 6169
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: Where get a Recent-Git-Build of FreeBASIC?

Postby counting_pine » Nov 10, 2012 20:52

It's probably enough for it to be implicitly defined. Just as long as it's not left for subclass destructors to call a null ptr.
fxm
Posts: 9014
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Abstract/Virtual destructor/method behaviour

Postby fxm » Nov 10, 2012 21:20

In Child destructor, If we put in comment the following line:
Base.S = ""
We get:

Code: Select all

free memory = 1055.5546875 MB

'Dim As Child Ptr c = New Child'
free memory = 905.1015625 MB

'Delete c'
free memory = 955.19921875 MB
The memory allocated to the string character data is not destroyed (none implicit destructor for Parent).
Last edited by fxm on Apr 12, 2015 9:21, edited 1 time in total.
dkl
Site Admin
Posts: 3207
Joined: Jul 28, 2005 14:45
Location: Germany

Re: Where get a Recent-Git-Build of FreeBASIC?

Postby dkl » Nov 10, 2012 21:34

That's a good point about ABSTRACT (pure-virtual) destructors that I hadn't thought about: The fields won't be destructed. I guess we need to disallow ABSTRACT destructors then, and only allow VIRTUAL ones that have a body that will always be called even if overridden as in C++.
counting_pine
Site Admin
Posts: 6169
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: Where get a Recent-Git-Build of FreeBASIC?

Postby counting_pine » Nov 10, 2012 21:46

(Just to be clear, I've talking about how I think things should work, rather than how they do work.)
That would be a bug I think, that abstract parent destructors are not called.

I think the point of abstract destructors is to force subclasses to implement their own cleanup code, in the same way that the point of abstract methods is to force subclasses to implement their own versions of them.
Only the destructor is a special case because of the fact that parent and member destructors must be called, so must still be defined, either explicitly or implicitly.
fxm
Posts: 9014
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Abstract/Virtual destructor/method behaviour

Postby fxm » Nov 11, 2012 10:39

The virtual destructor defined in Parent type does not call the implicit destructor in Child Type!

Code: Select all

Type Parent Extends Object
  Dim As String SP
  Dim As Byte BP(1 To 15 * 1024 * 1024)
  Declare Constructor ()
  Declare Virtual Destructor ()
End Type
Constructor Parent ()
  This.SP = Space(50 * 1024 * 1024)
End Constructor
Destructor Parent ()
End destructor

Type Child Extends Parent
  Dim As String SC
  Dim As Byte BC(1 To 30 * 1024 * 1024)
  Declare Constructor ()
End type
Constructor Child ()
  Base()
  This.SC = Space(100 * 1024 * 1024)
End Constructor

Print "free memory ="; fre / 1024 / 1024; " MB"
Print
Print "'Dim As Parent Ptr c = New Child'"
Dim As Parent Ptr c = New Child
Print "free memory ="; fre / 1024 / 1024; " MB"
Print
Print "'Delete c'"
Delete c
Print "free memory ="; fre / 1024 / 1024; " MB"
Sleep

Code: Select all

free memory = 1341.6640625 MB

'Dim As Parent Ptr c = New Child'
free memory = 1146.0703125 MB

'Delete c'
free memory = 1241.265625 MB
The string character data of Child.SC (100 MB) are not destroyed!

If we add an explicit empty destructor in Child type:

Code: Select all

Type Parent Extends Object
  Dim As String SP
  Dim As Byte BP(1 To 15 * 1024 * 1024)
  Declare Constructor ()
  Declare Virtual Destructor ()
End Type
Constructor Parent ()
  This.SP = Space(50 * 1024 * 1024)
End Constructor
Destructor Parent ()
End destructor

Type Child Extends Parent
  Dim As String SC
  Dim As Byte BC(1 To 30 * 1024 * 1024)
  Declare Constructor ()
  Declare Destructor ()
End type
Constructor Child ()
  Base()
  This.SC = Space(100 * 1024 * 1024)
End Constructor
Destructor Child ()
End destructor

Print "free memory ="; fre / 1024 / 1024; " MB"
Print
Print "'Dim As Parent Ptr c = New Child'"
Dim As Parent Ptr c = New Child
Print "free memory ="; fre / 1024 / 1024; " MB"
Print
Print "'Delete c'"
Delete c
Print "free memory ="; fre / 1024 / 1024; " MB"
Sleep

Code: Select all

free memory = 1334.8203125 MB

'Dim As Parent Ptr c = New Child'
free memory = 1139.203125 MB

'Delete c'
free memory = 1334.6015625 MB
Then the string character data of Child.SC (100 MB) are well destroyed, but by the implicit Child destructor called by the explicit empty Child destructor!
Is this logic?
Last edited by fxm on Apr 12, 2015 9:22, edited 1 time in total.
fxm
Posts: 9014
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Abstract/Virtual destructor/method behaviour

Postby fxm » Nov 11, 2012 15:35

fxm wrote:The virtual destructor defined in Parent type does not call the implicit destructor in Child Type!

The behavior is even more annoying!
The implicit Child destructor is "disabled" by the virtual Parent destructor, even when we try to delete the Child object by means of a Child type pointer!

Code: Select all

Type Parent Extends Object
  Dim As String SP
  Dim As Byte BP(1 To 15 * 1024 * 1024)
  Declare Constructor ()
  Declare Virtual Destructor ()
End Type
Constructor Parent ()
  This.SP = Space(50 * 1024 * 1024)
End Constructor
Destructor Parent ()
End destructor

Type Child Extends Parent
  Dim As String SC
  Dim As Byte BC(1 To 30 * 1024 * 1024)
  Declare Constructor ()
End type
Constructor Child ()
  Base()
  This.SC = Space(100 * 1024 * 1024)
End Constructor

Print "free memory ="; fre / 1024 / 1024; " MB"
Print
Print "'Dim As Child Ptr c = New Child'"
Dim As Child Ptr c = New Child
Print "free memory ="; fre / 1024 / 1024; " MB"
Print
Print "'Delete c'"
Delete c
Print "free memory ="; fre / 1024 / 1024; " MB"
Sleep

Code: Select all

free memory = 1142.89453125 MB

'Dim As Child Ptr c = New Child'
free memory = 947.296875 MB

'Delete c'
free memory = 1042.4921875 MB

If we suppress the virtual Parent destructor or if we add an empty Child destructor, then the implicit Child destructor is "re-enabled".

Workaround:
If a virtual base destructor is defined, all the derived types must have an explicit destructor (even empty).
Last edited by fxm on Apr 12, 2015 9:22, edited 1 time in total.
counting_pine
Site Admin
Posts: 6169
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: Where get a Recent-Git-Build of FreeBASIC?

Postby counting_pine » Nov 12, 2012 1:20

These bugs might be easier to detect with verbose reporting than with free memory checking, e.g. with a helper type like this that notifies when it is created/destroyed. This makes it easy to detect implicit destructors, while of course explicit destructors can report themselves.

Code: Select all

type notifier
  declare constructor(byval as zstring ptr = 0)
  declare destructor
  as zstring ptr txt
end type
constructor notifier(byval z as zstring ptr)
 txt = z
 print *txt & " ctor"
end constructor
destructor notifier()
  print "Implicit " & *txt & " dtor"
end destructor


Code: Select all

'' comment out 'virtual' for non-virtual
'' comment out whole line to remove explicit dtor
#define __parent_dtor__ virtual
#define __child_dtor__ virtual

type notifier
   declare constructor(byval as zstring ptr = 0): declare destructor: as zstring ptr txt
end type
constructor notifier(byval z as zstring ptr): txt = z: print *txt & " ctor": end constructor
destructor notifier: print "Implicit " & *txt & " dtor": end destructor


type parent extends object
   a as notifier => "Parent"
   #ifdef __parent_dtor__
      declare __parent_dtor__ destructor
   #endif
end type
#ifdef __parent_dtor__
   destructor parent: print "Explicit Parent dtor": end destructor
#endif


type child extends parent
   b as notifier => "Child"
   #ifdef __child_dtor__
      declare __child_dtor__ destructor
   #endif
end type
#ifdef __child_dtor__
   destructor child: print "Explicit Child dtor": end destructor
#endif

dim as parent ptr p = new child
print
delete p
The above code shows that a parent ptr can't call a virtual child destructor.
fxm
Posts: 9014
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Abstract/Virtual destructor/method behaviour

Postby fxm » Nov 12, 2012 8:39

counting_pine,

Well done.
But I did not same conclusion than yours (or is it due to my broken English!):
counting_pine wrote:The above code shows that a parent ptr can't call a virtual child destructor.

My conclusion is:
When there is a virtual parent destructor:
- A parent pointer cannot call the implicit child destructor.
- Even a child pointer cannot call the implicit child destructor.

Code: Select all

'' comment out 'virtual' for non-virtual
'' comment out whole line to remove explicit dtor
#define __parent_dtor__ virtual
'#define __child_dtor__ virtual

type notifier
   declare constructor(byval as zstring ptr = 0): declare destructor: as zstring ptr txt
end type
constructor notifier(byval z as zstring ptr): txt = z: print *txt & " ctor": end constructor
destructor notifier: print "Implicit " & *txt & " dtor": end destructor

type parent extends object
   a as notifier => "Parent"
   #ifdef __parent_dtor__
      declare __parent_dtor__ destructor
   #endif
end type
#ifdef __parent_dtor__
   destructor parent: print "Explicit Parent dtor": end destructor
#endif

type child extends parent
   b as notifier => "Child"
   #ifdef __child_dtor__
      declare __child_dtor__ destructor
   #endif
end type
#ifdef __child_dtor__
   destructor child: print "Explicit Child dtor": end destructor
#endif

dim as parent ptr p = new child
print
delete p
print
print "--------------------"
print
dim as child ptr c = new child
print
delete c
sleep

Code: Select all

Parent ctor
Child ctor

Explicit Parent dtor
Implicit Parent dtor

--------------------

Parent ctor
Child ctor

Explicit Parent dtor
Implicit Parent dtor
Last edited by fxm on Apr 12, 2015 9:23, edited 1 time in total.
fxm
Posts: 9014
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Abstract/Virtual destructor/method behaviour

Postby fxm » Nov 12, 2012 12:47

dkl wrote:That's a good point about ABSTRACT (pure-virtual) destructors that I hadn't thought about: The fields won't be destructed. I guess we need to disallow ABSTRACT destructors then, and only allow VIRTUAL ones that have a body that will always be called even if overridden as in C++.

We can also use the method of counting_pine in order to well verify this bad behavior:

Code: Select all

type notifier
   declare constructor(byval as zstring ptr = 0): declare destructor: as zstring ptr txt
end type
constructor notifier(byval z as zstring ptr): txt = z: print *txt & " ctor": end constructor
destructor notifier: print "Implicit " & *txt & " dtor": end destructor

type parent extends object
   a as notifier => "Parent"
   declare abstract destructor
end type

type child extends parent
   b as notifier => "Child"
   declare destructor
end type
destructor child: print "Explicit Child dtor": end destructor

dim as parent ptr p = new child
print
delete p
print
print "--------------------"
print
dim as child ptr c = new child
print
delete c
sleep

Code: Select all

Parent ctor
Child ctor

Explicit Child dtor
Implicit Child dtor

--------------------

Parent ctor
Child ctor

Explicit Child dtor
Implicit Child dtor

When there is an abstract parent destructor (and in accordance an explicit child destructor):
- A parent pointer cannot call the implicit parent destructor.
- A child pointer cannot call the implicit parent destructor.
Last edited by fxm on Apr 12, 2015 9:23, edited 1 time in total.
counting_pine
Site Admin
Posts: 6169
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: Where get a Recent-Git-Build of FreeBASIC?

Postby counting_pine » Nov 12, 2012 15:52

I will double-check the child ptr situation, but I think it gave different results for me..
I think declaring an abstract destructor should work, and do two things:
- require an explicit child constructor implementation (as with any abstract method)
- either require a explicit destructor body of the parent or disallow it and provide an implicit one. My understanding is, once any kind of destructor is declared, we wouldn't have the flexibility in the compiler to allow the programmer to choose, unless we added yet-another keyword like Explicit.

Return to “Community Discussion”

Who is online

Users browsing this forum: No registered users and 1 guest