Use Implicit / Overload New([]) and Delete([]) Operators with Inheritance Polymorphism


How to use the implicit or overload New and Delete operators and their New[] and Delete[] array-versions, with inheritance polymorphism (sub-type polymorphism), and how workaround some unexpected or unsuitable behaviors.

Preamble:
Some definitions and introductions to start with.

Different operators New and Delete (may be confusion in the user mind despite the documentation that distinguishes them from each other through different pages)

Implicit New/Delete operator (inaccessible by user):
- It is a static function/sub that only allocates/frees memory (it is not very different from Allocate/Deallocate).
Overload New/Delete operator (defined by user):
- It is a member operator (static function/sub) that can overload the 'Implicit New/Delete operator' only for user-defined types.
- So the user can define its own dynamic memory allocation/deallocation process part (the following/previous process part for implicit object construction/destruction can not be modified).

New Expression operator:
- It starts by using the 'Implicit/Overload New operator' (the implicit, or the overload if exists) to allocate memory.
- Then it invokes the constructor for the right type of object. If that object contains any other objects (either embedded or as base types) those constructors as invoked as well.
- So the final result is memory allocated and object constructed.
Delete Statement operator:
- It starts by invoking the destructor for the right type of object. If that object contains any other objects (either embedded or as base types) those destructors as invoked as well.
- Then it uses the 'Implicit/Overload Delete operator' (the implicit, or the overload if exists) to deallocate memory.
- So the final result is object destroyed and memory freed.

Placement New Operator:
- It constructs an object at a specified memory address (already allocated by another process).
- Consequently there is no 'Placement Delete operator'.
- It the object has a destructor (implicit or explicit), the user can call it with syntax as for a member method by using member access operator.

Similar definition for 'Implicit/overload New[]/Delete[] operators', 'New[] expression operator' and 'Delete[] statement operator', 'Placement New[] operator', which are only the (one-dimensional) array-versions of the previous operators (there is construction/destruction loop on the array elements).

Inheritance Polymorphism (ability of calling from the base type the member procedures of derived-types without worrying about the real type of the processed objects)

Thanks to the 'abstract'/'virtual' procedures, one can write a code using only the base type that will automatically call the derived-type procedures.
It is then possible to call the procedure of an object without worrying about its intrinsic type.

By using the same procedure name for several different types, the polymorphism allows a much more generic programming (abstraction).
The coder does not have to know, when calling a base procedure, the precise type of object on which the procedure will apply. He just needs to know that this type will implement the procedure.

Thus, a base-typed pointer (or reference), pointing to an instance of a derived-type, can be used to manipulate such an object.
Considering a collection of objects whose instantiate types are derived-types from a base type, then all these objects can be manipulated in an uniform way by considering them as objects of the base type.

Content of the following (6 parts and their associated examples)

6 main parts by increasing difficulty:
6 associated examples:
Starting from the same common body of polymorphism by inheritance (polymorphism by sub-type):
- Structure hierarchy: Animal as base Type, Cat and Dog as derived Types.
- Member data: string ('name') in Animal, string ('favorite') in Cat and in Dog.
- Procedure fields: abstract/virtual subs 'Init()', abstract/virtual functions 'get_attributes()', virtual destructors 'Destructor()'.
- Base constructor is protected and base copy-constructor is private, in order to disallow any construction or copy-construction for base object.
- 4 objects constructed: 2 instances of Cat, 2 instances of Dog.
Then, other member procedures are added according to the part to be processed, and to the unexpected or unsuitable behaviors to be workarounded.



1. Use Implicit New and Delete operators with inheritance polymorphism (sub-type polymorphism)
A collection of base-typed pointers in an array (base-typed Ptr array), where each pointer addresses a single derived object of any derived type.
No New/Delete operator overload.

This is the starting point of the study (simple case of polymorphism).
No unexpected or unsuitable behavior.

No unsuitable or unexpected behavior
Although the Implicit Delete operator is static, everything works fine when calling the static Delete statement on a base-typed pointer:
- The object is completely destroyed (derived part and base part) thanks to the virtual destructor in each derived type which overrides that of the base type.
- Then, the total memory is well deallocated even by calling the Implicit Delete operator of the base type, because this process only uses the value of the pointer provided.

Example
' Code for using implicit 'New'/'Delete' operators from base-typed pointer array in polymorphic inheritance context

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

Destructor Animal ()
    Print "Animal destructor: ", "object address: " & @This
End Destructor

Constructor Animal ()
    Print "Animal constructor: ", "object address: " & @This
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: ", "  object address: " & @This
End Constructor

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

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

Destructor Cat()
    Print "  Cat destructor: ", "  object address: " & @This
End Destructor


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: ", "  object address: " & @This
End Constructor

Sub Dog.Init(ByRef _name As String, ByRef _favorite As String)
    This.Name = _name
    This.favorite = _favorite
End Sub

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

Destructor Dog()
    Print "  Dog destructor: ", "  object address: " & @This
End Destructor

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

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
Output (64-bit):
Animal constructor:         object address: 16455952
  Cat constructor:            object address: 16455952
Animal constructor:         object address: 16473360
  Cat constructor:            object address: 16473360
Animal constructor:         object address: 16473424
  Dog constructor:            object address: 16473424
Animal constructor:         object address: 16473488
  Dog constructor:            object address: 16473488
	Tiger: Cat, Meow, Salmon
	Kitty: Cat, Meow, Sardine
	Buddy: Dog, Woof, Lamb
	Molly: Dog, Woof, Beef
  Cat destructor:             object address: 16455952
Animal destructor:          object address: 16455952
  Cat destructor:             object address: 16473360
Animal destructor:          object address: 16473360
  Dog destructor:             object address: 16473424
Animal destructor:          object address: 16473424
  Dog destructor:             object address: 16473488
Animal destructor:          object address: 16473488




2. Use Placement New operator with inheritance polymorphism (sub-type polymorphism)
A collection of base-typed pointers in an array (base-typed Ptr array), where each pointer addresses a single derived object of any derived type.
The memory allocation/deallocation is done by another process (Allocate/Deallocate).

From the previous example
For the memory allocation and object construction:
replace:
Dim As Animal Ptr pa(0 To ...) = {New Cat(), New Cat(), New Dog(), New Dog()}
with:
Dim As Any Ptr pc1 = Allocate(SizeOf(Cat))
Dim As Any Ptr pc2 = Allocate(SizeOf(Cat))
Dim As Any Ptr pd1 = Allocate(SizeOf(Dog))
Dim As Any Ptr pd2 = Allocate(SizeOf(Dog))

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

For the object destruction and memory deallocation:
replace:
For I As Integer = LBound(pa) To UBound(pa)
    Delete pa(I)
Next I
with:
For I As Integer = LBound(pa) To UBound(pa)
    pa(I)->Destructor()
Next I

Deallocate(pc1)
Deallocate(pc2)
Deallocate(pd1)
Deallocate(pd2)




3. Use Implicit New[] and Delete[] operators with inheritance polymorphism (sub-type polymorphism)
A collection of base-typed pointers in an array (base-typed Ptr array), where each pointer is a buffer pointer allowing to address several derived object of a same derived type.
No New[]/Delete[] operator overload.

One unsuitable behavior and one unexpected behavior are encountered
The calculation of the right address of any derived object 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:
- 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 the Implicit New[] operator, and from a base-typed pointer, a solution consists in overloading the '[]' operator, with a virtual '[]' operator in each derived type which overrides that (abstract) of the base type.
- Finally, to well call the overload '[]' operator (and not the implicit '[]' operator), it must be called on a dereferenced pointer (and not on the rough pointer that would called the implicit '[]' operator).
- If 'p' is the base-typed pointer, the right expression is '(*p)[n]' and not 'p[n]'.
- Warning: to calculate the right address of the nth derived object, the virtual '[]' operator is always called on the '*p' reference corresponding to the first object of the array buffer, so a correct overriding of the operator assumes that this first object of the array buffer is always a valid derived object (not destroyed for example).
The static Delete[] statement does not allow to retrieve the real run-time type of the object:
- So 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.
- However, the total memory would be well deallocated even by calling the Implicit Delete[] operator of the base type, because this process only uses the value of the pointer provided.
- The safest is to use a virtual launcher for the Delete[] statement in each derived type which overrides that (abstract) of the base type, and in this case (as by calling the Delete[] statement on a derived type pointer), this is not mandatory of defining a virtual destructor but remains still recommended.
- In the following example, the called virtual launcher is 'DeleteSB_launcher()'.

Example
' Code for using implicit 'New[]'/'Delete[]' operators from base-typed pointer array in polymorphic inheritance context
'    Added member procedures to workaround unsuitable or unexpected behaviors:
'       - Abstract/Virtual 'Operator []()' to access to the right address of any derived object from a base-typed pointer
'       - Abstract/Virtual 'DeleteSB_launcher()' to destroy the right objects from a base-typed pointer

Type Animal Extends Object
    Public:
        Declare Abstract Sub Init(ByRef _name As String, ByRef _favorite As String)
        Declare Abstract Function get_attributes() 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 ()
    Print "Animal destructor: ", "object address: " & @This
End Destructor

Constructor Animal ()
    Print "Animal constructor: ", "object address: " & @This
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: ", "  object address: " & @This
End Constructor

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

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

Destructor Cat()
    Print "  Cat destructor: ", "  object address: " & @This
End Destructor

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: ", "  object address: " & @This
End Constructor

Sub Dog.Init(ByRef _name As String, ByRef _favorite As String)
    This.Name = _name
    This.favorite = _favorite
End Sub

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

Destructor Dog()
  Print "  Dog destructor: ", "  object address: " & @This
End Destructor

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")   '' does not work
'pa(0)[1].Init("Kitty", "Sardine")  '' does not work
'pa(1)[0].Init("Buddy", "Lamb")     '' does not work
'pa(1)[1].Init("Molly", "Beef")     '' does not work
(*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()  '' does not work
        Print "    " & (*pa(I))[J].get_attributes()
    Next J
Next I

For I As Integer = LBound(pa) To UBound(pa)
'    Delete[] pa(I)  '' does not work
    pa(I)->DeleteSB_launcher()
Next I

Sleep
Output (64-bit):
Animal constructor:         object address: 7101688
  Cat constructor:            object address: 7101688
Animal constructor:         object address: 7101744
  Cat constructor:            object address: 7101744
Animal constructor:         object address: 7101816
  Dog constructor:            object address: 7101816
Animal constructor:         object address: 7101872
  Dog constructor:            object address: 7101872
	Tiger: Cat, Meow, Salmon
	Kitty: Cat, Meow, Sardine
	Buddy: Dog, Woof, Lamb
	Molly: Dog, Woof, Beef
  Cat destructor:             object address: 7101744
Animal destructor:          object address: 7101744
  Cat destructor:             object address: 7101688
Animal destructor:          object address: 7101688
  Dog destructor:             object address: 7101872
Animal destructor:          object address: 7101872
  Dog destructor:             object address: 7101816
Animal destructor:          object address: 7101816




4. Use Placement New[] operator with inheritance polymorphism (sub-type polymorphism)
A collection of base-typed pointers in an array (base-typed Ptr array), where each pointer is a buffer pointer allowing to address several derived object of a same derived type.
The memory allocation/deallocation is done by another process (Allocate/Deallocate).

From the previous example
For the memory allocation and object construction:
replace:
Dim As Animal Ptr pa(0 To ...) = {New Cat[2], New Dog[2]}
with:
Dim As Any Ptr pc = Allocate(2 * SizeOf(Cat))
Dim As Any Ptr pd = Allocate(2 * SizeOf(Dog))

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

For the object destruction and memory deallocation:
replace:
For I As Integer = LBound(pa) To UBound(pa)
'    Delete[] pa(I)  '' does not work
    pa(I)->DeleteSB_launcher()
Next I
with:
For I As Integer = LBound(pa) To UBound(pa)
    For J As Integer = 1 To 0 Step -1  '' reverse order for destruction is mandatory
                                       ''    (see warning on '[]' operator usage)
'        pa(I)[J]. Destructor()   '' does not work
        (*pa(I))[J].Destructor()
    Next J
Next I

Deallocate(pc)
Deallocate(pd)
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 as above, must always be done in the reverse order (end with the first object of the buffer).




5. Use Overload New and Delete operators with inheritance polymorphism (sub-type polymorphism)
A collection of base-typed pointers in an array (base-typed Ptr array), where each pointer addresses a single derived object of any derived type.
New/Delete operators overloaded.

One unexpected behavior is encountered
The static Delete statement does not allow to retrieve the real run-time type of the object:
- So, the user code of the overload Delete operator of the derived type is not executed because the Delete operator of the base type is called instead.
- The safest is to use a virtual launcher for the Delete statement in each derived type which overrides that (abstract) of the base type, and in this case (as by calling the Delete statement on a derived type pointer), this is not mandatory of defining a virtual destructor but remains still recommended.
- In the following example, the called virtual launcher is 'Delete_launcher()'.

Example
' Code for using overload 'New'/'Delete' operators from base-typed pointer array in polymorphic inheritance context
'    Added member procedure to workaround unexpected behavior:
'       - Abstract/Virtual 'Delete_launcher()' to call the overload Delete operator of the derived type from a base-typed pointer

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

Destructor Animal ()
    Print "  Animal destructor: ", "  object address: " & @This
End Destructor

Constructor Animal ()
    Print "  Animal constructor: ", "  object address: " & @This
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 Operator New(ByVal size As UInteger) As Any Ptr
        Declare Operator Delete(ByVal buf As Any Ptr)
        Declare Virtual Sub Delete_launcher()
    Private:
        Dim As String favorite
End Type

Constructor Cat ()
    Print "    Cat constructor: ", "    object address: " & @This
End Constructor

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

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

Destructor Cat()
    Print "    Cat destructor: ", "    object address: " & @This
End Destructor

Operator Cat.New(ByVal size As UInteger) As Any Ptr
    Dim As Any Ptr p = CAllocate(size)
    Print "Cat New operator: ", "buffer address: " & p
    Return p
End Operator

Operator Cat.Delete(ByVal buf As Any Ptr)
    Print "Cat Delete operator: ", "object address: " & buf
    Deallocate(buf)
End Operator

Sub Cat.Delete_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 Operator New(ByVal size As UInteger) As Any Ptr
        Declare Operator Delete(ByVal buf As Any Ptr)
        Declare Virtual Sub Delete_launcher()
    Private:
        Dim As String favorite
End Type

Constructor Dog()
    Print "    Dog constructor: ", "    object address: " & @This
End Constructor

Sub Dog.Init(ByRef _name As String, ByRef _favorite As String)
    This.Name = _name
    This.favorite = _favorite
End Sub

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

Destructor Dog()
    Print "    Dog destructor: ", "    object address: " & @This
End Destructor

Operator Dog.New(ByVal size As UInteger) As Any Ptr
    Dim As Any Ptr p = CAllocate(size)
    Print "Dog New operator: ", "buffer address: " & p
    Return p
End Operator

Operator Dog.Delete(ByVal buf As Any Ptr)
    Print "Dog Delete operator: ", "buffer address: " & buf
    Deallocate(buf)
End Operator

Sub Dog.Delete_launcher()
    Delete @This
End Sub

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

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)  '' does not work
    pa(I)->Delete_launcher()
Next I

Sleep
Output (64-bit):
Cat New operator:           buffer address: 13900048
  Animal constructor:         object address: 13900048
	Cat constructor:            object address: 13900048
Cat New operator:           buffer address: 13917456
  Animal constructor:         object address: 13917456
	Cat constructor:            object address: 13917456
Dog New operator:           buffer address: 13917520
  Animal constructor:         object address: 13917520
	Dog constructor:            object address: 13917520
Dog New operator:           buffer address: 13917584
  Animal constructor:         object address: 13917584
	Dog constructor:            object address: 13917584
	  Tiger: Cat, Meow, Salmon
	  Kitty: Cat, Meow, Sardine
	  Buddy: Dog, Woof, Lamb
	  Molly: Dog, Woof, Beef
	Cat destructor:             object address: 13900048
  Animal destructor:          object address: 13900048
Cat Delete operator:        object address: 13900048
	Cat destructor:             object address: 13917456
  Animal destructor:          object address: 13917456
Cat Delete operator:        object address: 13917456
	Dog destructor:             object address: 13917520
  Animal destructor:          object address: 13917520
Dog Delete operator:        buffer address: 13917520
	Dog destructor:             object address: 13917584
  Animal destructor:          object address: 13917584
Dog Delete operator:        buffer address: 13917584




6. Use Overload New[] and Delete[] operators with inheritance polymorphism (sub-type polymorphism)
A collection of base-typed pointers in an array (base-typed Ptr array), where each pointer is a buffer pointer allowing to address several derived object of a same derived type.
New[]/Delete[] operators overloaded.

One unsuitable behavior and one unexpected behavior are encountered
The calculation of the right address of any derived object 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:
- 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 the Overload New[] operator, and from a base-typed pointer, a solution consists in overloading the '[]' operator, with a virtual '[]' operator in each derived type which overrides that (abstract) of the base type.
- To well call the overload '[]' operator (and not the implicit '[]' operator), it must be called on a dereferenced pointer (and not on the rough pointer that would called the implicit '[]' operator).
- If 'p' is the base-typed pointer, the right expression is '(*p)[n]' and not 'p[n]'.
- Warning: to calculate the right address of the nth derived object, the virtual '[]' operator is always called on the '*p' reference corresponding to the first object of the array buffer, so a correct overriding of the operator assumes that this first object of the array buffer is always a valid derived object (not destroyed for example).
The static Delete[] statement does not allow to retrieve the real run-time type of the object:
- So 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.
- Also, the user code of the overload Delete[] operator of the derived type is not executed because the Delete[] operator of the base type is called instead.
- The safest for these two consequences is to always use a virtual launcher for the Delete[] statement in each derived type which overrides that (abstract) of the base type, and in this case (as by calling the Delete[] statement on a derived type pointer), this is not mandatory of defining a virtual destructor but remains still recommended.
- In the following example, the called virtual launcher is 'DeleteSB_launcher()'.

Example
' Code for using overload 'New[]'/'Delete[]' operators from base-typed pointer array in polymorphic inheritance context
'    Added member procedures to workaround unsuitable or unexpected behaviors:
'       - Abstract/Virtual 'Operator []()' to access to the right address of any derived object from a base-typed pointer
'       - Abstract/Virtual 'DeleteSB_launcher()' to destroy the right objects from a base-typed pointer, and to call the overload Delete[] operator of the derived type from a base-typed pointer

Type Animal Extends Object
    Public:
        Declare Abstract Sub Init(ByRef _name As String, ByRef _favorite As String)
        Declare Abstract Function get_attributes() 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 ()
    Print "  Animal destructor: ", "  object address: " & @This
End Destructor

Constructor Animal ()
    Print "  Animal constructor: ", "  object address: " & @This
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 Operator New[](ByVal size As UInteger) As Any Ptr
        Declare Operator Delete[](ByVal buf As Any Ptr)
        Declare Virtual Sub DeleteSB_launcher()
    Private:
        Dim As String favorite
End Type

Constructor Cat()
    Print "    Cat constructor: ", "    object address: " & @This
End Constructor

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

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

Destructor Cat()
    Print "    Cat destructor: ", "    object address: " & @This
End Destructor

Operator Cat.[](ByVal _n As Integer) ByRef As Cat
    Return (@This)[_n]
End Operator

Operator Cat.New[](ByVal size As UInteger) As Any Ptr
    Dim As Any Ptr p = CAllocate(size)
    Print "Cat New[] operator: ", "buffer address: " & p
    Return p
End Operator

Operator Cat.Delete[](ByVal buf As Any Ptr)
    Print "Cat Delete[] operator: ", "buffer address: " & buf
    Deallocate(buf)
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 Operator New[](ByVal size As UInteger) As Any Ptr
        Declare Operator Delete[](ByVal buf As Any Ptr)
        Declare Virtual Sub DeleteSB_launcher()
    Private:
        Dim As String favorite
End Type

Constructor Dog()
    Print "    Dog constructor: ", "    object address: " & @This
End Constructor

Sub Dog.Init(ByRef _name As String, ByRef _favorite As String)
    This.Name = _name
    This.favorite = _favorite
End Sub

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

Destructor Dog()
  Print "    Dog destructor: ", "    object address: " & @This
End Destructor

Operator Dog.[](ByVal _n As Integer) ByRef As Dog
    Return (@This)[_n]
End Operator

Operator Dog.New[](ByVal size As UInteger) As Any Ptr
    Dim As Any Ptr p = CAllocate(size)
    Print "Dog New[] operator: ", "buffer address: " & p
    Return p
End Operator

Operator Dog.Delete[](ByVal buf As Any Ptr)
    Print "Dog Delete[] operator: ", "buffer address: " & buf
    Deallocate(buf)
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")   '' does not work
'pa(0)[1].Init("Kitty", "Sardine")  '' does not work
'pa(1)[0].Init("Buddy", "Lamb")     '' does not work
'pa(1)[1].Init("Molly", "Beef")     '' does not work
(*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()  '' does not work
        Print "      " & (*pa(I))[J].get_attributes()
    Next J
Next I

For I As Integer = LBound(pa) To UBound(pa)
'    Delete[] pa(I)  '' does not work
    pa(I)->DeleteSB_launcher()
Next I

Sleep
Output (64-bit):
Cat New[] operator:         buffer address: 17128688
  Animal constructor:         object address: 17128696
	Cat constructor:            object address: 17128696
  Animal constructor:         object address: 17128752
	Cat constructor:            object address: 17128752
Dog New[] operator:         buffer address: 17128816
  Animal constructor:         object address: 17128824
	Dog constructor:            object address: 17128824
  Animal constructor:         object address: 17128880
	Dog constructor:            object address: 17128880
	  Tiger: Cat, Meow, Salmon
	  Kitty: Cat, Meow, Sardine
	  Buddy: Dog, Woof, Lamb
	  Molly: Dog, Woof, Beef
	Cat destructor:             object address: 17128752
  Animal destructor:          object address: 17128752
	Cat destructor:             object address: 17128696
  Animal destructor:          object address: 17128696
Cat Delete[] operator:      buffer address: 17128688
	Dog destructor:             object address: 17128880
  Animal destructor:          object address: 17128880
	Dog destructor:             object address: 17128824
  Animal destructor:          object address: 17128824
Dog Delete[] operator:      buffer address: 17128816
Note:
- As the derived type has a destructor, an extra uinteger is allocated by the New[] operator at the head of the memory buffer, in order to store the number of elements as part of the allocation, so that the Delete[] operator can determine the count of destructors to call.
- This is why the memory buffer address is different from the first object address in the memory buffer.




Conclusion:
To be compatible at the same time with any usage (simple/array-version, implicit/overload) of the New([])/Delete([]) operators in an inheritance polymorphism (sub-type polymorphism), 3 member procedures must be added to workaround unsuitable or unexpected behaviors:
- Abstract/Virtual 'Operator []()' to access to the right address of any derived object from a base-typed pointer.
- Abstract/Virtual 'Delete_launcher()' to call the overload Delete operator of the derived type from a base-typed pointer.
- Abstract/Virtual 'DeleteSB_launcher()' to destroy the objects at the right addresses from a base-typed pointer, and to call the overload Delete[] operator of the derived type from a base-typed pointer.

'Virtual' declarations and bodies of the added member procedures in each derived type, but only 'abstract' declarations in the base type:
Type base_type Extends Object
    Declare Abstract Operator [](ByVal n As Integer) ByRef As base_type
    Declare Abstract Sub Delete_launcher()
    Declare Abstract Sub DeleteSB_launcher()
End Type

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

Type derived_type Extends base_type
    Declare Virtual Operator [](ByVal n As Integer) ByRef As derived_type
    Declare Virtual Sub Delete_launcher()
    Declare Virtual Sub DeleteSB_launcher()
End Type

Operator derived_type.[](ByVal n As Integer) ByRef As derived_type
    Return (@This)[n]
End Operator

Sub derived_type.Delete_launcher()
    Delete @This
End Sub

Sub derived_type.DeleteSB_launcher()
    Delete[] @This
End Sub
Note:
- For the virtual '[]' operator in the derived type, declaring returning by derived-typed reference is not mandatory (just for aesthetics by using the covariance). Returning by base-typed reference is sufficient.
- The important thing is the derived operator body which uses a derived-typed pointer indexing, allowing to return a based-typed reference but rightly pointing to the derived object.

Abstract
- The New([]) expression returns a typed pointer corresponding to the called type (a derived type in our case), but the principle of inheritance polymorphism (sub-type polymorphism) is precisely to immediately up-cast this derived type to a base type.
- Using a base-typed pointer to a derived object buffer does not generally allows to correctly index the different addresses of the derived objects (except if all member data fields are in the base type only).
- The Implicit/Overload operators New([]) or Delete([]) are all static because their only role is to allocate or free memory, and at that time the object does not exist yet or no longer exists. So this is the Implicit/Overload Delete([]) operator of the base type which is called instead of the one of the derived type.
- Thus, to nevertheless use all these New([])/Delete([]) functionalities correctly without derogating from the exclusive usage of base-typed pointers, it is necessary to use a solution as developed above (adding one virtual overload operator and two virtual procedures).




See also:
Back to Programmer's Guide
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki



sf.net phatcode