How to use implicit / overload New([]) and Delete([]) operators in a polymorphic context

Forum for discussion about the documentation project.
Post Reply
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

How to use implicit / overload New([]) and Delete([]) operators in a polymorphic context

Post by fxm »

I have no problem when overloading the NEW / NEW [] and DELETE / DELETE [] operators for a single UDT or a derived UDT without using the polymorphism.

But the DELETE operator cannot be declared virtual (as the implicit DELETE operator and the NEW operator), so how delete a derived object by using a base type pointer referring to this derived object?

See my below test code:

Code: Select all

Type Parent Extends Object
  Dim As Integer I
  Declare Constructor ()
  Declare Virtual Destructor ()
  Declare Virtual Sub _Delete ()
  Declare Virtual Sub _DeleteSB ()
End Type

Constructor Parent ()
  Print "Parent.Constructor()",, @This
End Constructor

Destructor Parent ()
  Print "Parent.Destructor()",, @This
End Destructor

Sub Parent._Delete ()
  Delete @This
End Sub

Sub Parent._DeleteSB ()
  Delete[] @This
End Sub


Type Child Extends Parent
  Dim As Integer J
  Declare Operator New (Byval size As Uinteger) As Any Ptr
  Declare Operator Delete (Byval buf As Any Ptr)
  Declare Operator New[] (Byval size As Uinteger) As Any Ptr
  Declare Operator Delete[] (Byval buf As Any Ptr)
  Declare Constructor ()
  Declare Destructor ()
  Declare Virtual Sub _Delete ()
  Declare Virtual Sub _DeleteSB ()
End Type

Operator Child.New (Byval size As Uinteger) As Any Ptr
  Dim AS Any Ptr p = Callocate(size)
  Print "Child.New(Byval As Uinteger)", p, size
  Return p
End Operator

Operator Child.Delete (Byval buf As Any Ptr)
  Print "Child.Delete(Byval As Any Ptr)", buf
  Deallocate(buf)
End Operator

Operator Child.New[] (Byval size As Uinteger) As Any Ptr
  Dim AS Any Ptr p = Callocate(size)
  Print "Child.New[](Byval As Uinteger)", p, size
  Return p
End Operator

Operator Child.Delete[] (Byval buf As Any Ptr)
  Print "Child.Delete[](Byval As Any Ptr)", buf
  Deallocate(buf)
End Operator

Constructor Child ()
  Print "Child.Constructor()",, @This
End Constructor

Destructor Child ()
  Print "Child.Destructor()",, @This
End Destructor

Sub Child._Delete ()
  Delete @This
End Sub

Sub Child._DeleteSB ()
  Delete[] @This
End Sub


Dim As Child Ptr pcc1 = New Child
Print
Delete pcc1                                     '' works
Print
Print

Dim As Child Ptr pcc2 = New Child[2]
Print
Delete[] pcc2                                   '' works
Print
Print

Dim As Parent Ptr ppc1 = New Child
Print
'Delete Cptr(Child Ptr, ppc1)                   '' works
ppc1->_Delete()                                 '' works
Print
Print

Dim As Parent Ptr ppc2 = New Child[2]
Print
'Delete[] Cptr(Child Ptr, ppc2)                 '' works
ppc2->_DeleteSB()                               '' works

Sleep
@dkl
Is it normal or a missing feature?
Are one obliged to use a virtual launcher (_DeleteSB() method in my above code) or a down casting to call DELETE at the right location (the overloaded version)?
Last edited by fxm on Aug 20, 2018 8:05, edited 3 times in total.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How to use implicit / overload New([]) and Delete([]) operators in a polymorphic context

Post by fxm »

I tried to also overload the DELETE operators in Parent, but this is always the Parent DELETE overload which is called and even the program crashes (run-time error) on the call of the DELETE[] operator:

Code: Select all

Type Parent Extends Object
  Dim As Integer I
  Declare Operator Delete (Byval buf As Any Ptr)
  Declare Operator Delete[] (Byval buf As Any Ptr)
  Declare Constructor ()
  Declare Virtual Destructor ()
  Declare Virtual Sub _Delete ()
  Declare Virtual Sub _DeleteSB ()
End Type

Operator Parent.Delete (Byval buf As Any Ptr)
  Print "Parent.Delete(Byval As Any Ptr)", buf
  Deallocate(buf)
End Operator

Operator Parent.Delete[] (Byval buf As Any Ptr)
  Print "Parent.Delete[](Byval As Any Ptr)", buf
  Deallocate(buf)
End Operator

Constructor Parent ()
  Print "Parent.Constructor()",, @This
End Constructor

Destructor Parent ()
  Print "Parent.Destructor()",, @This
End Destructor

Sub Parent._Delete ()
  Delete @This
End Sub

Sub Parent._DeleteSB ()
  Delete[] @This
End Sub


Type Child Extends Parent
  Dim As Integer J
  Declare Operator New (Byval size As Uinteger) As Any Ptr
  Declare Operator Delete (Byval buf As Any Ptr)
  Declare Operator New[] (Byval size As Uinteger) As Any Ptr
  Declare Operator Delete[] (Byval buf As Any Ptr)
  Declare Constructor ()
  Declare Destructor ()
  Declare Virtual Sub _Delete ()
  Declare Virtual Sub _DeleteSB ()
End Type

Operator Child.New (Byval size As Uinteger) As Any Ptr
  Dim AS Any Ptr p = Callocate(size)
  Print "Child.New(Byval As Uinteger)", p, size
  Return p
End Operator

Operator Child.Delete (Byval buf As Any Ptr)
  Print "Child.Delete(Byval As Any Ptr)", buf
  Deallocate(buf)
End Operator

Operator Child.New[] (Byval size As Uinteger) As Any Ptr
  Dim AS Any Ptr p = Callocate(size)
  Print "Child.New[](Byval As Uinteger)", p, size
  Return p
End Operator

Operator Child.Delete[] (Byval buf As Any Ptr)
  Print "Child.Delete[](Byval As Any Ptr)", buf
  Deallocate(buf)
End Operator

Constructor Child ()
  Print "Child.Constructor()",, @This
End Constructor

Destructor Child ()
  Print "Child.Destructor()",, @This
End Destructor

Sub Child._Delete ()
  Delete @This
End Sub

Sub Child._DeleteSB ()
  Delete[] @This
End Sub


Dim As Parent Ptr ppc1 = New Child
Print
Delete ppc1
Print
Print

Dim As Parent Ptr ppc2 = New Child[2]
Print
Delete[] ppc2

Sleep

Code: Select all

Child.New(Byval As Uinteger)              7411816       12
Parent.Constructor()                      7411816
Child.Constructor()                       7411816

Child.Destructor()                        7411816
Parent.Destructor()                       7411816
Parent.Delete(Byval As Any Ptr)           7411816


Child.New[](Byval As Uinteger)            7417688       28
Parent.Constructor()                      7417692
Child.Constructor()                       7417692
Parent.Constructor()                      7417704
Child.Constructor()                       7417704


Aborting due to runtime error 12 ("segmentation violation" signal) in D:\Users\T
0003830\Documents\Mes Outils Personnels\FBIde0.4.6r4_fbc1.06.0\FBIDETEMP.bas::()
[edit]

Looking at C++ standard (ISO/IEC 14882:2003(E)), §12.5-7, it seems that:

- For the overload DELETE operator (not the array operator):
If the Parent destructor is virtual, the effect is the same between:
Delete Cast(Child Ptr, ppc1)
and
Delete ppc1
for the object destruction only, but not the same for the data deallocation (overload operator DELETE not called at Child level in case of the second syntax).

- For the overload DELETE[] operator (the array operator):
Delete[] Cast(Child Ptr, ppc2)
works, but for:
Delete[] ppc2
the behavior is in general undefined (when the size of the static type is different of the one of the dynamic type?).
This seems to work (concerning the object destruction only) for an array of one object or for a child type without data field, but not for the data deallocation (overload operator DELETE[] not called at Child level in case of the second syntax).

The static Delete([]) operator does not allow to retrieve the real run-time type of the object, so the called Delete([]) operator (implicit or overload) is not the one at the correct level in the inheritance hierarchy.
In addition, for the Delete[] operator (array version), the calculation of the address of each object to destroy (if it is necessary) is generally false (because it takes into account as object size the one corresponding to the pointer type and not the real object type), except obviously for the first object.
This undefined behavior is a non obvious case of more generally attempting to perform pointer arithmetic on a polymorphic object value, because the calculated offset is based on the pointer type and not the real object type. It would not come to the idea of dereference with an offset index a basic pointer to access the derived object as with 'ppc2[1].I'.

In conclusion for polymorphism, the safest is to always use a virtual launcher for DELETE[] and for the overload DELETE, and in this case (as by calling DELETE on a child-type pointer), defining a virtual destructor is not mandatory.
Last edited by fxm on Aug 22, 2018 7:54, edited 21 times in total.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How to use implicit / overload New([]) and Delete([]) operators in a polymorphic context

Post by fxm »

Added note in documentation, because maybe it's an unsolvable problem as existing in C++ (see C++ standard (ISO/IEC 14882:2003(E)), §5.3.5-3 and §12.5-7), and therefore this behavior can persist:
  • KeyPgOpDelete → fxm [Added note on incompatibility between any operator Delete[] (or the only overload operator Delete) and polymorphism]
Added bug report anyway, but without much hope:
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How to use implicit / overload New([]) and Delete([]) operators in a polymorphic context

Post by fxm »

fxm wrote:The static Delete([]) operator does not allow to retrieve the real run-time type of the object, so the called Delete([]) operator (implicit or overload) is not the one at the correct level in the inheritance hierarchy.
In addition, for the Delete[] operator (array version), the calculation of the address of each object to destroy (if it is necessary) is generally false (because it takes into account as object size the one corresponding to the pointer type and not the real object type), except obviously for the first object.
This undefined behavior is a non obvious case of more generally attempting to perform pointer arithmetic on a polymorphic object value, because the calculated offset is based on the pointer type and not the real object type. It would not come to the idea of dereference with an offset index a basic pointer to access the derived object as with 'ppc2[1].I'.

In conclusion for polymorphism, the safest is to always use a virtual launcher for DELETE[] and for the overload DELETE, and in this case (as by calling DELETE on a child-type pointer), defining a virtual destructor is not mandatory.
To access to the right address of any derived object (when derived type contains one data field at least) in the array buffer constructed by New[], from a buffer base pointer or a buffer base reference, a solution consists in overloading the '[]' operator, both in the base and derived types (with overriding).

Then, for accessing from a buffer base pointer, the good derived object address for the nth element is:
@(*base_pointer)[n-1]
and not:
@base_pointer[n-1]

Or, for accessing from a buffer base reference, the good derived object address for the nth element is:
@base_reference[n-1]
and not:
@(@base_pointer)[n-1]

Example using a buffer base pointer and a buffer base reference:

Code: Select all

Type Parent Extends Object
  Dim As Integer I
  Declare Constructor ()
  Declare Virtual Destructor ()
  Declare Virtual Sub _Delete ()
  Declare Virtual Sub _DeleteSB ()
  Declare Virtual Operator [] (Byval n As Integer) Byref As Parent
End Type

Constructor Parent ()
  Print "    Pointer value for intance Parent construction: " & @This
End Constructor

Destructor Parent ()
  Print "    Pointer value for intance Parent destruction: " & @This
End Destructor

Sub Parent._Delete ()
  Print "  Parent._Delete ()"
  Delete @This
End Sub

Sub Parent._DeleteSB ()
  Print "  Parent._DeleteSB ()"
  Delete[] @This
End Sub

Operator Parent.[] (Byval n As Integer) Byref As Parent
  Return (@This)[n]
End Operator



Type Child Extends Parent
  Dim As Integer J
  Declare Operator New (Byval size As Uinteger) As Any Ptr
  Declare Operator New[] (Byval size As Uinteger) As Any Ptr
  Declare Constructor ()
  Declare Virtual Destructor ()
  Declare Operator Delete (Byval buffer As Any Ptr)
  Declare Operator Delete[] (Byval buffer As Any Ptr)
  Declare Virtual Sub _Delete ()
  Declare Virtual Sub _DeleteSB ()
  Declare Virtual Operator [] (Byval n As Integer) Byref As Child
  Private:
    Declare Static Function allocation (ByVal size As UInteger) As Any Ptr
    Declare Static Sub deallocation (ByVal buffer As Any Ptr)
End Type

Operator Child.New (Byval size As Uinteger) As Any Ptr
  Print "  Overload Child.New (Byval size As Uinteger) As Any Ptr"
  Return Child.allocation(size)
End Operator

Operator Child.New[] (Byval size As Uinteger) As Any Ptr
  Print "  Overload Child.New[] (Byval size As Uinteger) As Any Ptr"
  Return Child.allocation(size)
End Operator

Constructor Child ()
  Print "    Pointer value for intance Child construction: " & @This
End Constructor

Destructor Child ()
  Print "    Pointer value for intance Child destruction: " & @This
End Destructor

Operator Child.Delete (Byval buffer As Any Ptr)
  Print "  Overload Child.Delete (Byval buffer As Any Ptr)"
  Child.deallocation(buffer)
End Operator

Operator Child.Delete[] (Byval buffer As Any Ptr)
  Print "  Overload Child.Delete[] (Byval buffer As Any Ptr)"
  Child.deallocation(buffer)
End Operator

Sub Child._Delete ()
  Print "  Child._Delete ()"
  Delete @This
End Sub

Sub Child._DeleteSB ()
  Print "  Child._DeleteSB ()"
  Delete[] @This
End Sub

Operator Child.[] (Byval n As Integer) Byref As Child
  Return (@This)[n]
End Operator

Function Child.allocation (ByVal size As UInteger) As Any Ptr
  Dim As Any Ptr p = Allocate(size)
  Print "  Pointer value returned from allocation request of " &size & " Bytes: " & p
  If p = 0 Then
    Print "Memory allocation failed"
    Sleep
    End
  End If
  Return p
End Function

Sub Child.deallocation (ByVal buffer As Any Ptr)
  Print "  Requested memory deallocation on pointer value: " & buffer
  Deallocate(buffer)
End Sub 



#ifdef __FB_64BIT__
  Print "64";
#else
  Print "32";
#endif
Print "-bit code",
Print "Integer size: " & Sizeof(Integer) & " Bytes"
Print



Print "'Dim As Parent Ptr ppc2 = New Child[3]'"
Dim As Parent Ptr ppc2 = New Child[3]

Print "      bad object address:", @(ppc2)[0], @(ppc2)[1], @(ppc2)[2]      '' does not work (pointer arthmetic not valid with base pointer)
Print "      good object address:", @(*ppc2)[0], @(*ppc2)[1], @(*ppc2)[2]  '' works

'Print "'Delete[] ppc2'"  
'Delete[] ppc2            '' does not work (programm crashes by "segmentation violation")

'Print "'Delete[] Cptr(Child Ptr, ppc2)'"
'Delete[] Cptr(Child Ptr, ppc2)            '' works

Print "'ppc2->_DeleteSB()'"
ppc2->_DeleteSB()            '' works
Print



Print "'Dim Byref As Parent rpc2 = *New Child[3]'"
Dim Byref As Parent rpc2 = *New Child[3]

Print "      bad object address:", @(@rpc2)[0], @(@rpc2)[1], @(@rpc2)[2]  '' does not work (pointer arthmetic not valid with base pointer)
Print "      good object address:", @rpc2[0], @rpc2[1], @rpc2[2]          '' works

'Print "'Delete[] @rpc2'"
'Delete[] @rpc2            '' does not work (programm crashes by "segmentation violation")

'Print "'Delete[] Cptr(Child Ptr, @rpc2)'"
'Delete[] Cptr(Child Ptr, @rpc2)           '' works

Print "'rpc2._DeleteSB()'"
rpc2._DeleteSB()                                           '' works
Print

Sleep

Code: Select all

32-bit code   Integer size: 4 Bytes

'Dim As Parent Ptr ppc2 = New Child[3]'
  Overload Child.New[] (Byval size As Uinteger) As Any Ptr
  Pointer value returned from allocation request of 40 Bytes: 4795008
    Pointer value for intance Parent construction: 4795012
    Pointer value for intance Child construction: 4795012
    Pointer value for intance Parent construction: 4795024
    Pointer value for intance Child construction: 4795024
    Pointer value for intance Parent construction: 4795036
    Pointer value for intance Child construction: 4795036
      bad object address:   4795012       4795020       4795028
      good object address:  4795012       4795024       4795036
'ppc2->_DeleteSB()'
  Child._DeleteSB ()
    Pointer value for intance Child destruction: 4795036
    Pointer value for intance Parent destruction: 4795036
    Pointer value for intance Child destruction: 4795024
    Pointer value for intance Parent destruction: 4795024
    Pointer value for intance Child destruction: 4795012
    Pointer value for intance Parent destruction: 4795012
  Overload Child.Delete[] (Byval buffer As Any Ptr)
  Requested memory deallocation on pointer value: 4795008

'Dim Byref As Parent rpc2 = *New Child[3]'
  Overload Child.New[] (Byval size As Uinteger) As Any Ptr
  Pointer value returned from allocation request of 40 Bytes: 4795008
    Pointer value for intance Parent construction: 4795012
    Pointer value for intance Child construction: 4795012
    Pointer value for intance Parent construction: 4795024
    Pointer value for intance Child construction: 4795024
    Pointer value for intance Parent construction: 4795036
    Pointer value for intance Child construction: 4795036
      bad object address:   4795012       4795020       4795028
      good object address:  4795012       4795024       4795036
'rpc2._DeleteSB()'
  Child._DeleteSB ()
    Pointer value for intance Child destruction: 4795036
    Pointer value for intance Parent destruction: 4795036
    Pointer value for intance Child destruction: 4795024
    Pointer value for intance Parent destruction: 4795024
    Pointer value for intance Child destruction: 4795012
    Pointer value for intance Parent destruction: 4795012
  Overload Child.Delete[] (Byval buffer As Any Ptr)
  Requested memory deallocation on pointer value: 4795008
In addition (after obtain the right object address), for accessing to a derived field, it is the general principle when using polymorphism, by defining override procedures (subs, functions, properties, operators, ...).
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: How to use implicit / overload New([]) and Delete([]) operators in a polymorphic context

Post by dodicat »

Couldn't you use
delete[]cast(child ptr,ppc2)
and
Delete[] cast(child ptr,@rpc2)

instead of creating the _delete methods.
You have the delete[] already made for the child.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How to use implicit / overload New([]) and Delete([]) operators in a polymorphic context

Post by fxm »

Yes, I wrote the equivalent in my code (lines put in comment), on a longer form than necessary (because stupidly copied from my first post), but it's a manual down-casting at compile time and not an automatic process at runtime.
If we define an array of base pointer referring to Child1 and Child2 and Child3 ... derived objects, we cannot write a generic loop without using a polymorphic process.

Pseudo-code example:

Code: Select all

Dim As Parent Ptr pp(0 To 3) = {New Child1[3], New Child2[5], New Child3[4], New Child4[6]}

.....
.....

'Delete[] Cptr(Child1 Ptr, pp(0))
'Delete[] Cptr(Child2 Ptr, pp(1))
'Delete[] Cptr(Child3 Ptr, pp(2))
'Delete[] Cptr(Child4 Ptr, pp(3))
For I As Integer = Lbound(pp) To Ubound(pp)
  pp(I)->_DeleteSB()
Next I
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: How to use implicit / overload New([]) and Delete([]) operators in a polymorphic context

Post by dodicat »

To be honest fxm with these memory allocations de-allocations I test using a create/destroy loop, and run the resource monitor for a few minutes.
I suppose you do the same before posting.
I sometimes test mem calls with the crt malloc/free also, after all these are really the calls being made.
But I wouldn't expect allocate and delete to work together anyway.
Here was my crude run with your code.

Code: Select all

 

'#include "crt.bi"
dim shared as long b=0
type p extends object 
    as integer i
    declare   constructor
    declare virtual destructor()
    Declare virtual Operator [] (Byval n As Integer) Byref As P
    Declare Virtual Sub _DeleteSB ()
end type

constructor p
if b then print  "constructor p"
end constructor

destructor p()
if b then print "destructor p"
end destructor


Operator P.[] (Byval n As Integer) Byref As P
if b then print "OP[]"
  Return (@This)[n]
End Operator


Sub P._DeleteSB ()
 if b then  Print "  Parent._DeleteSB ()"
  Delete[] @This
End Sub

type q extends p 
    as integer j
     Declare Constructor ()
     declare virtual destructor 
     Declare Operator New[] (Byval size As Uinteger) As Any Ptr
     Declare virtual Operator [] (Byval n As Integer) Byref As q 
     Declare Operator Delete[] (Byval buffer As Any Ptr) 
      Declare Virtual Sub _DeleteSB ()
end type

constructor q
if b then print "constructor q"
end constructor

destructor q
if b then print "destructor q"
end destructor

Operator q.New[] (Byval size As Uinteger) As Any Ptr
 if b then  Print "  Overload Child.New";
  return allocate(size)'malloc
End Operator

Operator q.[] (Byval n As Integer) Byref As q
 if b then  Return (@This)[n]
End Operator

Operator q.Delete[] (Byval buffer As Any Ptr)
 if b then  Print "  Overload Child.Delete"
  Deallocate(buffer)
 ' free(buffer)
End Operator

Sub q._DeleteSB ()
 if b then  Print "  Child._DeleteSB ()"
  Delete[] @This
End Sub
dim as long counter
do
    counter+=1
    locate 2
    print counter
dim as p ptr z=new q[30000]
delete[] cast(q ptr,z) 

'sleep
'z->_DeleteSB
loop until inkey=chr(27)

sleep  
Sorry to reduce your finely tuned code to this rough and ready testing.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How to use implicit / overload New([]) and Delete([]) operators in a polymorphic context

Post by fxm »

dodicat wrote:To be honest fxm with these memory allocations de-allocations I test using a create/destroy loop, and run the resource monitor for a few minutes.
I suppose you do the same before posting.
I do it only when I have doubts about my allocations / deallocations of memory.
There, I had no doubt, given the debug texts sent to the console that validate the various calls.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How to use implicit / overload New([]) and Delete([]) operators in a polymorphic context

Post by fxm »

I had prepared a series of examples for using 'New ([])'/'Delete ([])' in a polymorphic context, but like the proposed article "17. How to Use Implicit / Overload New ( []) and Delete ([]) FB Operators with Simple UDT up to Subtype Polymorphism" will not be written (see the discussion about that from post viewtopic.php?p=250918#p250918), it is still better to store the examples into this very technical topic.

1) Context

Structure hierarchy:
- base Type: Animal
- derived Types: Cat, Dog

Data fields:
- a string ('name') in Animal
- a string ('favorite) in Cat and in Dog

Procedure fields:
- an abstract/virtual sub: 'Init (Byref _name As String, Byref _favorite As String)'
- an abstract/virtual function: 'get_attributes() As String'
- a virtual destructor: 'Destructor ()'
in addition for 'New[]'/'Delete[]' usage:
- an abstract/virtual [] operator: 'Operator [] (Byval _n As Integer) Byref As Animal/Cat/Dog' (covariant Byref return)
- an abstract/virtual sub: 'DeleteSB_Launcher ()'

4 objects constructed:
- 2 instances of Cat
- 2 instances of Dog

8 contexts of code:
- 4 for 'New'/'Delete': using base pointers, or base references, or base pointer array, or base reference array
- 4 for 'New[]'/'Delete[]': using base pointers, or base references, or base pointer array, or base reference array
(a macro simulates the reference fix-len array feature)

2) Examples

- All 8 codes output the same text (if working) on console:

Code: Select all

Cat constructor
Cat constructor
Dog constructor
Dog constructor
  Tiger: Cat Meow Salmon
  Kitty: Cat Meow Sardine
  Buddy: Dog Woof Lamb
  Molly: Dog Woof Beef
Cat destructor
Cat destructor
Dog destructor
Dog destructor
- Code for 'New'/'Delete' and using base pointers in a polymorphic context:

Code: Select all

Type Animal Extends Object
  Public:
    Declare Abstract Function get_attributes() As String
    Declare Abstract Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Destructor ()
  Protected:
    Dim As String name
    Declare Constructor ()
  Private:
    Declare Constructor (Byref _a As Animal)
End Type

Destructor Animal ()
End Destructor

Constructor Animal ()
End Constructor


Type Cat Extends Animal
  Public:
    Declare Constructor ()
    Declare Virtual Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Function get_attributes() As String
    Declare Virtual Destructor ()
  Private:
    Dim As String favorite
End Type

Constructor Cat ()
  Print "Cat constructor"
End Constructor

Sub Cat.Init (Byref _name As String, Byref _favorite As String = "")
  This.name = _name
  This.favorite = _favorite
End Sub

Destructor Cat ()
  Print "Cat destructor"
End destructor

Function Cat.get_attributes() As String
  Return This.name & ": Cat Meow " & This.favorite
End Function


Type Dog Extends Animal
  Public:
    Declare Constructor ()
    Declare Virtual Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Function get_attributes() As String
    Declare Virtual Destructor ()
  Private:
    Dim As String favorite
End Type

Constructor Dog ()
  Print "Dog constructor"
End Constructor

Sub Dog.Init (Byref _name As String, Byref _favorite As String)
  This.name = _name
  This.favorite = _favorite
End Sub

Destructor Dog ()
  Print "Dog destructor"
End destructor

Function Dog.get_attributes() As String
  Return This.name & ": Dog Woof " & This.favorite
End Function

'------------------------------------------------------------------------------

Dim As Animal Ptr pac1 = New Cat()
Dim As Animal Ptr pac2 = New Cat()
Dim As Animal Ptr pad1 = New Dog()
Dim As Animal Ptr pad2 = New Dog()

pac1->Init("Tiger", "Salmon")
pac2->Init("Kitty", "Sardine")
pad1->Init("Buddy", "Lamb")
pad2->Init("Molly", "Beef")

Print "  " & pac1->get_attributes()
Print "  " & pac2->get_attributes()
Print "  " & pad1->get_attributes()
Print "  " & pad2->get_attributes()

Delete pac1
Delete pac2
Delete pad1
Delete pad2

Sleep
- Code for 'New'/'Delete' and using base references in a polymorphic context:

Code: Select all

Type Animal Extends Object
  Public:
    Declare Abstract Function get_attributes() As String
    Declare Abstract Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Destructor ()
  Protected:
    Dim As String name
    Declare Constructor ()
  Private:
    Declare Constructor (Byref _a As Animal)
End Type

Destructor Animal ()
End Destructor

Constructor Animal ()
End Constructor


Type Cat Extends Animal
  Public:
    Declare Constructor ()
    Declare Virtual Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Function get_attributes() As String
    Declare Virtual Destructor ()
  Private:
    Dim As String favorite
End Type

Constructor Cat ()
  Print "Cat constructor"
End Constructor

Sub Cat.Init (Byref _name As String, Byref _favorite As String = "")
  This.name = _name
  This.favorite = _favorite
End Sub

Destructor Cat ()
  Print "Cat destructor"
End destructor

Function Cat.get_attributes() As String
  Return This.name & ": Cat Meow " & This.favorite
End Function


Type Dog Extends Animal
  Public:
    Declare Constructor ()
    Declare Virtual Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Function get_attributes() As String
    Declare Virtual Destructor ()
  Private:
    Dim As String favorite
End Type

Constructor Dog ()
  Print "Dog constructor"
End Constructor

Sub Dog.Init (Byref _name As String, Byref _favorite As String)
  This.name = _name
  This.favorite = _favorite
End Sub

Destructor Dog ()
  Print "Dog destructor"
End destructor

Function Dog.get_attributes() As String
  Return This.name & ": Dog Woof " & This.favorite
End Function

'------------------------------------------------------------------------------

Dim Byref As Animal rac1 = *New Cat()
Dim Byref As Animal rac2 = *New Cat()
Dim Byref As Animal rad1 = *New Dog()
Dim Byref As Animal rad2 = *New Dog()

rac1.Init("Tiger", "Salmon")
rac2.Init("Kitty", "Sardine")
rad1.Init("Buddy", "Lamb")
rad2.Init("Molly", "Beef")

Print "  " & rac1.get_attributes()
Print "  " & rac2.get_attributes()
Print "  " & rad1.get_attributes()
Print "  " & rad2.get_attributes()

Delete @rac1
Delete @rac2
Delete @rad1
Delete @rad2

Sleep
- Code for 'New'/'Delete' and using base pointer array in a polymorphic context:

Code: Select all

Type Animal Extends Object
  Public:
    Declare Abstract Function get_attributes() As String
    Declare Abstract Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Destructor ()
  Protected:
    Dim As String name
    Declare Constructor ()
  Private:
    Declare Constructor (Byref _a As Animal)
End Type

Destructor Animal ()
End Destructor

Constructor Animal ()
End Constructor


Type Cat Extends Animal
  Public:
    Declare Constructor ()
    Declare Virtual Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Function get_attributes() As String
    Declare Virtual Destructor ()
  Private:
    Dim As String favorite
End Type

Constructor Cat ()
  Print "Cat constructor"
End Constructor

Sub Cat.Init (Byref _name As String, Byref _favorite As String = "")
  This.name = _name
  This.favorite = _favorite
End Sub

Destructor Cat ()
  Print "Cat destructor"
End destructor

Function Cat.get_attributes() As String
  Return This.name & ": Cat Meow " & This.favorite
End Function


Type Dog Extends Animal
  Public:
    Declare Constructor ()
    Declare Virtual Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Function get_attributes() As String
    Declare Virtual Destructor ()
  Private:
    Dim As String favorite
End Type

Constructor Dog ()
  Print "Dog constructor"
End Constructor

Sub Dog.Init (Byref _name As String, Byref _favorite As String)
  This.name = _name
  This.favorite = _favorite
End Sub

Destructor Dog ()
  Print "Dog destructor"
End destructor

Function Dog.get_attributes() As String
  Return This.name & ": Dog Woof " & This.favorite
End Function

'------------------------------------------------------------------------------

Dim As Animal Ptr pa(0 To ...) = {New Cat(), New Cat(), New Dog(), New Dog()}

pa(0)->Init("Tiger", "Salmon")
pa(1)->Init("Kitty", "Sardine")
pa(2)->Init("Buddy", "Lamb")
pa(3)->Init("Molly", "Beef")

For I As Integer = Lbound(pa) To Ubound(pa)
  Print "  " & pa(I)->get_attributes()
Next I

For I As Integer = Lbound(pa) To Ubound(pa)
  Delete pa(I)
Next I

Sleep
- Code for 'New'/'Delete' and using base reference array in a polymorphic context:

Code: Select all

Type Animal Extends Object
  Public:
    Declare Abstract Function get_attributes() As String
    Declare Abstract Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Destructor ()
  Protected:
    Dim As String name
    Declare Constructor ()
  Private:
    Declare Constructor (Byref _a As Animal)
End Type

Destructor Animal ()
End Destructor

Constructor Animal ()
End Constructor


Type Cat Extends Animal
  Public:
    Declare Constructor ()
    Declare Virtual Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Function get_attributes() As String
    Declare Virtual Destructor ()
  Private:
    Dim As String favorite
End Type

Constructor Cat ()
  Print "Cat constructor"
End Constructor

Sub Cat.Init (Byref _name As String, Byref _favorite As String = "")
  This.name = _name
  This.favorite = _favorite
End Sub

Destructor Cat ()
  Print "Cat destructor"
End destructor

Function Cat.get_attributes() As String
  Return This.name & ": Cat Meow " & This.favorite
End Function


Type Dog Extends Animal
  Public:
    Declare Constructor ()
    Declare Virtual Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Function get_attributes() As String
    Declare Virtual Destructor ()
  Private:
    Dim As String favorite
End Type

Constructor Dog ()
  Print "Dog constructor"
End Constructor

Sub Dog.Init (Byref _name As String, Byref _favorite As String)
  This.name = _name
  This.favorite = _favorite
End Sub

Destructor Dog ()
  Print "Dog destructor"
End destructor

Function Dog.get_attributes() As String
  Return This.name & ": Dog Woof " & This.favorite
End Function

'------------------------------------------------------------------------------

'Macro simulating the declaration with initialization of a fixed-length array of references
'(element-name: 'refName(I)', array-name: 'refName_Array')
  #macro DimByRefFixLenArrayInit (typeName, refName, lowBound, objectAddressList...)
    Dim As typeName Ptr ptr##refName(lowBound to ...) = { objectAddressList }
    #Define refName(n) (*ptr##refName(n))
    #Define refName##_Array ptr##refName
  #endmacro

'Dim Byref As Animal ra(0 To ...) = {*New Cat(), *New Cat(), *New Dog(), *New Dog()}
DimByRefFixLenArrayInit(Animal, ra, 0, New Cat(), New Cat(), New Dog(), New Dog())

ra(0).Init("Tiger", "Salmon")
ra(1).Init("Kitty", "Sardine")
ra(2).Init("Buddy", "Lamb")
ra(3).Init("Molly", "Beef")

For I As Integer = Lbound(ra_Array) To Ubound(ra_Array)
  Print "  " & ra(I).get_attributes()
Next I

For I As Integer = Lbound(ra_Array) To Ubound(ra_Array)
  Delete @ra(I)
Next I

Sleep
- Code for 'New[]'/'Delete[]' and using base pointers in a polymorphic context:

Code: Select all

Type Animal Extends Object
  Public:
    Declare Abstract Function get_attributes() As String
    Declare Abstract Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Destructor ()
    Declare Abstract Operator [] (Byval _n As Integer) Byref As Animal
    Declare Abstract Sub DeleteSB_Launcher ()
  Protected:
    Dim As String name
    Declare Constructor ()
  Private:
    Declare Constructor (Byref _a As Animal)
End Type

Destructor Animal ()
End Destructor

Constructor Animal ()
End Constructor


Type Cat Extends Animal
  Public:
    Declare Constructor ()
    Declare Virtual Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Function get_attributes() As String
    Declare Virtual Destructor ()
    Declare Virtual Operator [] (Byval _n As Integer) Byref As Cat
    Declare Virtual Sub DeleteSB_Launcher ()
  Private:
    Dim As String favorite
End Type

Constructor Cat ()
  Print "Cat constructor"
End Constructor

Sub Cat.Init (Byref _name As String, Byref _favorite As String = "")
  This.name = _name
  This.favorite = _favorite
End Sub

Destructor Cat ()
  Print "Cat destructor"
End destructor

Function Cat.get_attributes() As String
  Return This.name & ": Cat Meow " & This.favorite
End Function

Operator Cat.[] (Byval _n As Integer) Byref As Cat
  Return (@This)[_n]
End Operator

Sub Cat.DeleteSB_Launcher ()
  Delete[] @This
End Sub


Type Dog Extends Animal
  Public:
    Declare Constructor ()
    Declare Virtual Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Function get_attributes() As String
    Declare Virtual Destructor ()
    Declare Virtual Operator [] (Byval _n As Integer) Byref As Dog
    Declare Virtual Sub DeleteSB_Launcher ()
  Private:
    Dim As String favorite
End Type

Constructor Dog ()
  Print "Dog constructor"
End Constructor

Sub Dog.Init (Byref _name As String, Byref _favorite As String)
  This.name = _name
  This.favorite = _favorite
End Sub

Destructor Dog ()
  Print "Dog destructor"
End destructor

Function Dog.get_attributes() As String
  Return This.name & ": Dog Woof " & This.favorite
End Function

Operator Dog.[] (Byval _n As Integer) Byref As Dog
  Return (@This)[_n]
End Operator

Sub Dog.DeleteSB_Launcher ()
  Delete[] @This
End Sub

'------------------------------------------------------------------------------

Dim As Animal Ptr pac = New Cat[2]
Dim As Animal Ptr pad = New Dog[2]

(*pac)[0].Init("Tiger", "Salmon")
(*pac)[1].Init("Kitty", "Sardine")
(*pad)[0].Init("Buddy", "Lamb")
(*pad)[1].Init("Molly", "Beef")

For I As Integer = 0 To 1
  Print "  " & (*pac)[I].get_attributes()
Next I
For I As Integer = 0 To 1
  Print "  " & (*pad)[I].get_attributes()
Next I

pac->DeleteSB_Launcher()
pad->DeleteSB_Launcher()

Sleep
- Code for 'New[]'/'Delete[]' and using base references in a polymorphic context:

Code: Select all

Type Animal Extends Object
  Public:
    Declare Abstract Function get_attributes() As String
    Declare Abstract Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Destructor ()
    Declare Abstract Operator [] (Byval _n As Integer) Byref As Animal
    Declare Abstract Sub DeleteSB_Launcher ()
  Protected:
    Dim As String name
    Declare Constructor ()
  Private:
    Declare Constructor (Byref _a As Animal)
End Type

Destructor Animal ()
End Destructor

Constructor Animal ()
End Constructor


Type Cat Extends Animal
  Public:
    Declare Constructor ()
    Declare Virtual Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Function get_attributes() As String
    Declare Virtual Destructor ()
    Declare Virtual Operator [] (Byval _n As Integer) Byref As Cat
    Declare Virtual Sub DeleteSB_Launcher ()
  Private:
    Dim As String favorite
End Type

Constructor Cat ()
  Print "Cat constructor"
End Constructor

Sub Cat.Init (Byref _name As String, Byref _favorite As String = "")
  This.name = _name
  This.favorite = _favorite
End Sub

Destructor Cat ()
  Print "Cat destructor"
End destructor

Function Cat.get_attributes() As String
  Return This.name & ": Cat Meow " & This.favorite
End Function

Operator Cat.[] (Byval _n As Integer) Byref As Cat
  Return (@This)[_n]
End Operator

Sub Cat.DeleteSB_Launcher ()
  Delete[] @This
End Sub


Type Dog Extends Animal
  Public:
    Declare Constructor ()
    Declare Virtual Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Function get_attributes() As String
    Declare Virtual Destructor ()
    Declare Virtual Operator [] (Byval _n As Integer) Byref As Dog
    Declare Virtual Sub DeleteSB_Launcher ()
  Private:
    Dim As String favorite
End Type

Constructor Dog ()
  Print "Dog constructor"
End Constructor

Sub Dog.Init (Byref _name As String, Byref _favorite As String)
  This.name = _name
  This.favorite = _favorite
End Sub

Destructor Dog ()
  Print "Dog destructor"
End destructor

Function Dog.get_attributes() As String
  Return This.name & ": Dog Woof " & This.favorite
End Function

Operator Dog.[] (Byval _n As Integer) Byref As Dog
  Return (@This)[_n]
End Operator

Sub Dog.DeleteSB_Launcher ()
  Delete[] @This
End Sub

'------------------------------------------------------------------------------

Dim Byref As Animal rac = *New Cat[2]
Dim Byref As Animal rad = *New Dog[2]

rac[0].Init("Tiger", "Salmon")
rac[1].Init("Kitty", "Sardine")
rad[0].Init("Buddy", "Lamb")
rad[1].Init("Molly", "Beef")

For I As Integer = 0 To 1
  Print "  " & rac[I].get_attributes()
Next I
For I As Integer = 0 To 1
  Print "  " & rad[I].get_attributes()
Next I

rac.DeleteSB_Launcher()
rad.DeleteSB_Launcher()

Sleep
- Code for 'New[]'/'Delete[]' and using base pointer array in a polymorphic context:

Code: Select all

Type Animal Extends Object
  Public:
    Declare Abstract Function get_attributes() As String
    Declare Abstract Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Destructor ()
    Declare Abstract Operator [] (Byval _n As Integer) Byref As Animal
    Declare Abstract Sub DeleteSB_Launcher ()
  Protected:
    Dim As String name
    Declare Constructor ()
  Private:
    Declare Constructor (Byref _a As Animal)
End Type

Destructor Animal ()
End Destructor

Constructor Animal ()
End Constructor


Type Cat Extends Animal
  Public:
    Declare Constructor ()
    Declare Virtual Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Function get_attributes() As String
    Declare Virtual Destructor ()
    Declare Virtual Operator [] (Byval _n As Integer) Byref As Cat
    Declare Virtual Sub DeleteSB_Launcher ()
  Private:
    Dim As String favorite
End Type

Constructor Cat ()
  Print "Cat constructor"
End Constructor

Sub Cat.Init (Byref _name As String, Byref _favorite As String = "")
  This.name = _name
  This.favorite = _favorite
End Sub

Destructor Cat ()
  Print "Cat destructor"
End destructor

Function Cat.get_attributes() As String
  Return This.name & ": Cat Meow " & This.favorite
End Function

Operator Cat.[] (Byval _n As Integer) Byref As Cat
  Return (@This)[_n]
End Operator

Sub Cat.DeleteSB_Launcher ()
  Delete[] @This
End Sub


Type Dog Extends Animal
  Public:
    Declare Constructor ()
    Declare Virtual Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Function get_attributes() As String
    Declare Virtual Destructor ()
    Declare Virtual Operator [] (Byval _n As Integer) Byref As Dog
    Declare Virtual Sub DeleteSB_Launcher ()
  Private:
    Dim As String favorite
End Type

Constructor Dog ()
  Print "Dog constructor"
End Constructor

Sub Dog.Init (Byref _name As String, Byref _favorite As String)
  This.name = _name
  This.favorite = _favorite
End Sub

Destructor Dog ()
  Print "Dog destructor"
End destructor

Function Dog.get_attributes() As String
  Return This.name & ": Dog Woof " & This.favorite
End Function

Operator Dog.[] (Byval _n As Integer) Byref As Dog
  Return (@This)[_n]
End Operator

Sub Dog.DeleteSB_Launcher ()
  Delete[] @This
End Sub

'------------------------------------------------------------------------------

Dim As Animal Ptr pa(0 To ...) = {New Cat[2], New Dog[2]}

(*pa(0))[0].Init("Tiger", "Salmon")
(*pa(0))[1].Init("Kitty", "Sardine")
(*pa(1))[0].Init("Buddy", "Lamb")
(*pa(1))[1].Init("Molly", "Beef")

For I As Integer = Lbound(pa) To Ubound(pa)
  For J As Integer = 0 To 1
    Print "  " & (*pa(I))[J].get_attributes()
  Next J
Next I

For I As Integer = Lbound(pa) To Ubound(pa)
  pa(I)->DeleteSB_Launcher()
Next I

Sleep
- Code for 'New[]'/'Delete[]' and using base reference array in a polymorphic context:

Code: Select all

Type Animal Extends Object
  Public:
    Declare Abstract Function get_attributes() As String
    Declare Abstract Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Destructor ()
    Declare Abstract Operator [] (Byval _n As Integer) Byref As Animal
    Declare Abstract Sub DeleteSB_Launcher ()
  Protected:
    Dim As String name
    Declare Constructor ()
  Private:
    Declare Constructor (Byref _a As Animal)
End Type

Destructor Animal ()
End Destructor

Constructor Animal ()
End Constructor


Type Cat Extends Animal
  Public:
    Declare Constructor ()
    Declare Virtual Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Function get_attributes() As String
    Declare Virtual Destructor ()
    Declare Virtual Operator [] (Byval _n As Integer) Byref As Cat
    Declare Virtual Sub DeleteSB_Launcher ()
  Private:
    Dim As String favorite
End Type

Constructor Cat ()
  Print "Cat constructor"
End Constructor

Sub Cat.Init (Byref _name As String, Byref _favorite As String = "")
  This.name = _name
  This.favorite = _favorite
End Sub

Destructor Cat ()
  Print "Cat destructor"
End destructor

Function Cat.get_attributes() As String
  Return This.name & ": Cat Meow " & This.favorite
End Function

Operator Cat.[] (Byval _n As Integer) Byref As Cat
  Return (@This)[_n]
End Operator

Sub Cat.DeleteSB_Launcher ()
  Delete[] @This
End Sub


Type Dog Extends Animal
  Public:
    Declare Constructor ()
    Declare Virtual Sub Init (Byref _name As String, Byref _favorite As String)
    Declare Virtual Function get_attributes() As String
    Declare Virtual Destructor ()
    Declare Virtual Operator [] (Byval _n As Integer) Byref As Dog
    Declare Virtual Sub DeleteSB_Launcher ()
  Private:
    Dim As String favorite
End Type

Constructor Dog ()
  Print "Dog constructor"
End Constructor

Sub Dog.Init (Byref _name As String, Byref _favorite As String)
  This.name = _name
  This.favorite = _favorite
End Sub

Destructor Dog ()
  Print "Dog destructor"
End destructor

Function Dog.get_attributes() As String
  Return This.name & ": Dog Woof " & This.favorite
End Function

Operator Dog.[] (Byval _n As Integer) Byref As Dog
  Return (@This)[_n]
End Operator

Sub Dog.DeleteSB_Launcher ()
  Delete[] @This
End Sub

'------------------------------------------------------------------------------

'Macro simulating the declaration with initialization of a fixed-length array of references
'(element-name: 'refName(I)', array-name: 'refName_Array')
  #macro DimByRefFixLenArrayInit (typeName, refName, lowBound, objectAddressList...)
    Dim As typeName Ptr ptr##refName(lowBound to ...) = { objectAddressList }
    #Define refName(n) (*ptr##refName(n))
    #Define refName##_Array ptr##refName
  #endmacro

'Dim Byref As Animal ra(0 To ...) = {*New Cat[2], *New Dog[2]}
DimByRefFixLenArrayInit(Animal, ra, 0, New Cat[2], New Dog[2])

ra(0)[0].Init("Tiger", "Salmon")
ra(0)[1].Init("Kitty", "Sardine")
ra(1)[0].Init("Buddy", "Lamb")
ra(1)[1].Init("Molly", "Beef")

For I As Integer = Lbound(ra_Array) To Ubound(ra_Array)
  For J As Integer = 0 To 1
    Print "  " & ra(I)[J].get_attributes()
  Next J
Next I

For I As Integer = Lbound(ra_Array) To Ubound(ra_Array)
  ra(I).DeleteSB_Launcher()
Next I

Sleep
Last edited by fxm on Aug 25, 2018 6:24, edited 2 times in total.
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: How to use implicit / overload New([]) and Delete([]) operators in a polymorphic context

Post by coderJeff »

fxm, thanks for pointing me to all the information. Very helpful.

Short answer: As you suspect, unsolvable.

- static overloaded new/delete/new[]/delete[] is for memory allocation & deallocation only, the object doesn't exist,
- compare implicit new/delete, calls to malloc()/free(). free() does not care if it is the base or derived type pointer, it is same memory address.
- new[] returns a pointer type, and as a pointer type, we should expect that pointer math will work, except,
- main problem is when sizeof(basetype) <> sizeof(derivedtype), pointer math is wrong.
- but, should be safe with polymorphic interfaces, that is, all data is in the base type only.

So, to use all these features together, at once, need to use a solution like you have developed in this topic.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How to use implicit / overload New([]) and Delete([]) operators in a polymorphic context

Post by fxm »

New[] / Delete[] and polymorphism
  • Indeed, the main problem is for the destruction of derived objects (if destruction is needed), from a base pointer/reference.

    The problem occurs only if the three following conditions are true simultaneously:
    - ( destructor(s) (implicit or explicit) must be called )
    - AND ( Sizeof(derived type) > Sizeof(base type) )
    - AND ( number of objects > 1 )
    In that case, the address calculation (for destruction) of the second object (and the followings) is false.
New / Delete, New[] / Delete[], and polymorphism
  • Another problem is added if the user defines its own overload Delete or Delete[] operator for the derived type:
    - This operator will not be called, but the one (implicit or explicit) of the base type.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How to use implicit / overload New([]) and Delete([]) operators in a polymorphic context

Post by fxm »

coderJeff wrote:...
- but, should be safe with polymorphic interfaces, that is, all data is in the base type only.
...
I do not understand!

IMHO:
Generally an interface uses an "abstract" class:
- without any data fields
- with only abstract declared procedures that the user class implementing the interface must define all
In general, it is the user class implementing the interface that contains own data fields (directly, indirectly, or by inheritance from other classes).

[edit]
After thinking (also looking at the comment of your bug report), I see that what you call "interface" does not correspond to the known class INTERFACE ... END INTERFACE, but is rather a simple derived type (without data fields) grouping public procedures for a specific user (assuming that they are also declared as abstract/virtual in the base type for polymorphism capacity).
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: How to use implicit / overload New([]) and Delete([]) operators in a polymorphic context

Post by coderJeff »

Maybe there is a better term other than "polymorphic interface", but I don't know what else to call it; I guess I am thinking of interface in a general sense. Imagaine you have TYPE X. From that you have derived Mutable_X, Immutable_X, Serial_X, ThreadSafe_X, Network_X, or whatever specific derived behaviour you want. The low level data is manipulated in the BASE X TYPE only. Like in this example:

Code: Select all

type X extends object
	private:
		m_value as integer
	protected:
		declare sub setValue( new_value as integer )
		declare function getValue() as integer
end type

sub X.setValue( new_value as integer )
	m_value = new_value
end sub

function X.getValue() as integer
	function = m_value
end function

type mutable_X extends X
	declare property value( new_value as integer )
	declare property value() as integer
end type

property mutable_X.Value( new_value as integer)
	base.setValue( new_value )
end property

property mutable_X.Value() as integer
	property = base.getValue()
end property

type immutable_X extends X
	declare property value() as integer
end type

property immutable_X.Value() as integer
	property = base.getValue()
end property

dim a as X
a.setValue( 1 )    '' illegal member access
print a.getValue() '' illegal member access

dim b as mutable_X
b.value = 1        '' OK
print b.value      '' OK

dim c as immutable_X
c.value = 1        '' PROPERTY has no SET method/accessor
print c.value      '' OK
These kinds of derived type should work very well with new[]/delete[], no crashes, pointer math works, [] operator works, etc.

We have copied behaviour from C++ in to fbc. So what I have been doing is, read all the posts, read lots of C++ specification, then try to figure out why the feature exists in C++ to give an idea of why it exists in fbc. What I have found is that the C++ specification describes the rules as in how a feature should work, but not WHY should (or should not) use the feature.

And I think understanding why we use the things, helps us to also understand why things dont' work together.

For example,

Code: Select all

dim a as integer = 5
dim b as integer ptr = @a
b[42] = 7
This is perfectly valid code, while at the same time, most certainly unsafe. Why is this allowed? Well, 'b' is a pointer type, b[42] is valid, and the compiler can't determine that it is unsafe.

So I think we are seeing a similar scenario with new[]/delete[] and derived types that differ in size.

Here is my opinion:
- new[]/delete[] creates arrays of objects (calls constructors/destructors) - should only be used for trivial types, or where sizeof(base_type)=sizeof(derived_type), so the pointer math will work.
- overloaded new/delete/[] - the base type and derived types must all call the same memory allocator/deallocator to guarantee that the memory refers to the same memory pool. That is, these overloaded operators override memory allocation/deallocation specifically.

Maybe the reserved keyword "CLASS" will be the structure and syntax that does everything correctly. :) ... some day...
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How to use implicit / overload New([]) and Delete([]) operators in a polymorphic context

Post by fxm »

Documentation update:
- KeyPgOpNew → fxm [Added note highlighting the risk to use NEW[] ... DELETE[] in some cases]
- KeyPgOpPlacementNew → fxm [Added note highlighting the risk to use PLACEMENT NEW[] in some cases]
- KeyPgOpDelete → fxm [Added note on incompatibility between any operator Delete[] (or the only overload operator Delete) and sub-type polymorphism]

See also the post 'Confusion in the documentation and perhaps in the minds' in documentation forum.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How to use implicit / overload New([]) and Delete([]) operators in a polymorphic context

Post by fxm »

Warning when using the virtual operator '[]' in a context of inheritance polymorphism (sub-type polymorphism):
  • To calculate the correct address of the nth derived object in a buffer, from a base-typed pointer 'p', a virtual operator '[]' can be used to return (using: 'Return (@This)[n]') a reference to this nth derived object.
  • This virtual operator '[]' is always called on the same reference ('*p') corresponding to the first object of the buffer (operator called by: '(*p)[n]').
  • So, a correct overriding of this operator (to calculate the correct address of the nth derived object) assumes that the first object in the buffer is always a valid derived object (not destroyed for example).
  • This is why the destruction loop of the objects of the buffer (by: '(*p)[n].Destructor()'), in case of 'Placement New[]' usage for example, must always be done in the reverse order (end with the first object of the buffer).

    A small code which allows to show what not to do, and even to play with it:

    Code: Select all

    Type Parent Extends Object
        Dim As Integer I = 1234567890
        Declare Abstract Operator [](Byval n As Integer) Byref As Parent
        Declare Constructor()
        Declare Virtual Destructor()
    End Type
    
    Constructor Parent()
        Print "Parent.Constructor ", @This
    End Constructor
    
    Destructor Parent()
        Print "Parent.Destructor ", @This
    End Destructor
    
    Type Child Extends Parent
        Dim As Integer J
        Declare Virtual Operator [](Byval n As Integer) Byref As Parent
        Declare Constructor()
        Declare Virtual Destructor()
    End Type
    
    Operator Child.[](Byval n As Integer) Byref As Parent
        Return (@This)[n]
    End Operator
    
    Constructor Child()
        Print "   Child.Constructor ", @This
    End Constructor
    
    Destructor Child()
        Print "   Child.Destructor ", @This
    End destructor
    
    Dim As Any Ptr p0 = Allocate(3 * Sizeof(Child))
    
    Dim As Parent Ptr p = New(p0) Child[3]
    
    For n As Integer = 0 To 2  '' works
        Print "     ";
    '    Print p[n].I          '' does not work (object address is wrong from second object)
        Print (*p)[n].I        '' works (virtual Child '[]' operator is called)
    Next n
    
    'For n As Integer = 0 To 2  '' does not work (abstract Parent '[]' operator is called from second object => null pointer)
    ''    p[n].Destructor()     '' does not work (object address is wrong from second object)
    '    (*p)[n].Destructor()   '' should work (virtual Child '[]' operator should be called)
    'Next n
    
    For n As Integer = 2 To 0 Step -1  '' works (reverse order)
    '    p[n].Destructor()             '' does not work (object address is wrong from second object)
        (*p)[n].Destructor()           '' works (virtual Child '[]' operator is called)
    Next n
    
    Deallocate(p0)
    
    Sleep
    
Last edited by fxm on Dec 10, 2022 17:28, edited 6 times in total.
Post Reply