FreeBASIC マニュアルのトップに戻る

FreeBASIC ProPgUseNewDelete

目次→教本→プログラマーのための案内Use Implicit / Overload New([]) and Delete([]) Operators with Inheritance Polymorphism←オリジナル・サイト

継承ポリモーフィズムで、暗黙/多重定義のNew([])演算子,Delete([])演算子を使う 左にメニュー・フレームが表示されていない場合は、ここをクリックして下さい

←リンク元に戻る プログラム開発関連に戻る

暗黙的または多重定義された New 演算子Delete 演算子とその配列版である New[]Delete[] を、継承多相性(サブタイプ多相性)で使用し、いくつかの予想外のまたは不適切な動作を回避する方法について説明します。

序文:
最初にいくつかの定義と紹介。

New と Delete 演算子の違い (文書では別のページで区別していますが、ユーザーの印象で混乱する可能性があります)。

暗黙の New/Delete 演算子 (ユーザーはアクセスできません):
- メモリの割り当て/解放のみを行う静的関数/サブルーチンです (割り当て/割り当て解除と大差ありません)。
New/Delete 演算子の多重定義 (ユーザー定義):
- メンバー演算子(静的関数/サブルーチン)で、ユーザー定義型のみ「暗黙の New/Delete 演算子」を多重定義できます。
- したがって、ユーザーは独自の動的メモリ割り当て/解放処理部分を定義できます (暗黙のオブジェクト構築/破壊のための後続/前処理部分は変更できません)。

New 式 演算子:
- 'Implicit/Overload New operator' (暗黙的、または存在すれば多重定義) を使用してメモリを割り当てることから始まります。
- 次に、正しい型のオブジェクトのコンストラクターを呼び出します。そのオブジェクトに他のオブジェクト (埋め込み型または基本型として) が含まれている場合、それらのコンストラクターも呼び出されます。
- したがって、最終的な結果は、メモリが割り当てられ、オブジェクトが構築されます。
Delete 命令文 演算子:
- 適切なタイプのオブジェクトのデストラクタを呼び出すことから始めます。そのオブジェクトに他のオブジェクト (埋め込み型または基本型として) が含まれている場合、それらのデストラクタも呼び出されます。
- 次に、「暗黙的/多重定義削除演算子」(暗黙的、または存在する場合は多重定義) を使用して、メモリの割り当てを解除します。
- したがって、最終結果はオブジェクトが破棄され、メモリが解放されます。

配置 New 演算子:
- 指定されたメモリ アドレス (別のプロセスによって既に割り当てられている) にオブジェクトを構築します。
- したがって、「配置 Delete 演算子」はありません。
- オブジェクトにデストラクタ (暗黙的または明示的) がある場合、ユーザーはメンバー アクセス演算子を使って、メンバー メソッドと同様の構文で呼び出すことができます。

「暗黙/多重定義 New[]/Delete[] 演算子」、「New[] 式 演算子」、「Delete[] 命令文 演算子」、「配置 New[] 演算子」についても同様の定義で、以前の演算子の(1次元)配列版でしかありません (配列要素に対する構築/破棄ループは存在します)。

継承ポリモーフィズム (処理されたオブジェクトの実際の型を気にせずに、基本型から派生型のメンバー 手続きを呼び出す機能)

「抽象」/「仮想」手続きのおかげで、派生型手続きを自動的に呼び出す基本型のみを使ってコードを作成できます。
これにより、組み込み型を気にせずにオブジェクトの手続きを呼び出すことができます。

いくつかの異なる型に同じ手続き名を使うことにより、ポリモーフィズムは、より汎用的なプログラミング (抽象化) が可能になります。
コーダーは、基本手続きを呼び出すときに、手続きが適用されるオブジェクトの正確なタイプを知る必要はありません。ただ、この型が手続きを実装していることを知っているだけでよいのです。

したがって、派生型のインスタンスを指す基本型のポインター (または参照) を使って、このようなオブジェクトを操作できます。
インスタンス化された型が基本型から派生した型を持つオブジェクトの集合を考えるとき、これらのオブジェクトはすべて基本型のオブジェクトとみなすことで、統一的に操作できます。

以下の内容 (6つの項目とその関連例)

難易度別に6つの項目:
関連する 6 つの例:
継承によるポリモーフィズムの同じ共通本体から開始します (サブタイプによるポリモーフィズム):
- 構造階層: 基本型としての動物、派生型としての猫と犬。
- メンバー データ: Animal の文字列 ('name')、Cat および Dog の文字列 ('favorite')。
- 手続き フィールド: 抽象/仮想サブルーチン 'Init()'、抽象/仮想関数 'get_attributes()'、仮想デストラクタ 'Destructor()'。
- ベースオブジェクトの構築やコピーを禁止するため、ベースコンストラクタは保護され、ベースコピーコンストラクタは private に設定されています。
- 構築された 4 つのオブジェクト: 猫の 2 つのインスタンス、犬の 2 つのインスタンス。
そして、処理する部品や、想定外の動作や回避すべき動作に応じて、他のメンバー手続きを追加します。



1. 暗黙の New と Delete 演算子を継承ポリモーフィズム (サブタイプ ポリモーフィズム) で使う
配列内の基本型ポインターのコレクション (基本型 Ptr 配列)。各ポインターは、任意の派生型の単一の派生オブジェクトをアドレス指定します。
New/Delete 演算子の多重定義はありません。

これが研究の出発点です (多型の単純なケース)。
予期しない、または不適切な動作はありません。

不適切な動作や予期しない動作がないこと
Implicit Delete 演算子は静的ですが、基本型のポインターで静的な Delete ステートメントを呼び出すと、すべてが正常に機能します:
- オブジェクトは、派生型の仮想デストラクタが基底型の仮想デストラクタを上書きすることで、派生型と基底型が完全に破壊されます。
- そして、この処理では、提供されたポインタの値のみを使うため、ベース型の Implicit Delete オペレータを呼び出しても、全体のメモリが適切に解除されます。


' 多相継承文脈で基本型付きポインター配列から暗黙の 'New'/'Delete' 演算子を使うコード

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. 配置 New 演算子を継承ポリモーフィズム (サブタイプ ポリモーフィズム) で使う
配列内の基本型ポインターのコレクション (基本型 Ptr 配列)。各ポインターは、任意の派生型の単一の派生オブジェクトをアドレス指定します。
メモリの割り当て/割り当て解除は、別のプロセス (割り当て/割り当て解除) で行われます。

前の例から
メモリ割り当てとオブジェクト構築の場合:
変更前:
Dim As Animal Ptr pa(0 To ...) = {New Cat(), New Cat(), New Dog(), New Dog()}
変更後:
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 I As Integer = LBound(pa) To UBound(pa)
    Delete pa(I)
Next I
変更後:
For I As Integer = LBound(pa) To UBound(pa)
    pa(I)->Destructor()
Next I

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

先頭に戻る



3. 暗黙のNew[]演算子、Delete[]演算子を継承ポリモーフィズム(サブタイプポリモーフィズム)で使う
配列内の基本型ポインターのコレクション (基本型 Ptr 配列)。各ポインターは、同じ派生型の複数の派生オブジェクトをアドレス指定できるバッファー ポインターです。
New[]/Delete[] 演算子の多重定義はありません。

1つの不適切な動作と 1つの予期しない動作が発生しました
派生オブジェクトの正しいアドレスの計算は、明らかに最初のオブジェクトを除いて、一般に誤りです(オブジェクトサイズとして、実際のオブジェクトタイプではなく、ポインタタイプに対応するものを考慮するため):
- Implicit New[] 演算子によって構築された配列バッファー内の任意の派生オブジェクト (派生型に少なくとも 1 つのデータ フィールドが含まれる場合) の正しいアドレスにアクセスするには、基本型付きポインターから、' []' 演算子。各派生型の仮想 '[]' 演算子は、基本型の (抽象) を上書きします。
- 最後に、オーバーロード '[]' 演算子 (暗黙の '[]' 演算子ではない) を適切に呼び出すには、間接参照されたポインター (暗黙の '[]' 演算子を呼び出すラフ ポインターではなく) で呼び出す必要があります。 )。
- 'p' が基底型のポインターの場合、正しい式は '(*p)[n]' であり、'p[n]' ではありません。
- 警告: n番目の派生オブジェクトの正しいアドレスを計算するために、仮想の'[]'演算子は常に配列バッファの最初のオブジェクトに対応する '*p' 参照で呼び出されます。 したがって、この演算子を正しく上書きするには、配列バッファの最初のオブジェクトが常に有効な派生オブジェクトである必要があります(例えば、破壊されていない)。
静的な Delete[] ステートメントでは、オブジェクトの実際のランタイム タイプを取得できません:
- そのため、破壊する各オブジェクトのアドレスの計算は(それが必要な場合)、明らかに最初のオブジェクトを除いて、一般に誤りです(オブジェクトサイズとして、実際のオブジェクトタイプではなく、ポインタタイプに対応するものを考慮するため)。
- ただし、このプロセスは提供されたポインターの値しか使わないので、基本型の Implicit Delete[] 演算子を呼び出しても、メモリ全体の割り当ては適切に解除されます。
- 最も安全なのは、基本型の (抽象) を上書きする各派生型の Delete[] ステートメントに仮想ランチャーを使用することです。 この場合(派生型のポインタでDelete[]ステートメントを呼ぶ場合)、仮想デストラクタの定義は必須ではありませんが、推奨されることに変わりはありません。
- 以下の例では、呼び出される仮想ランチャーは「DeleteSB_launcher()」です。


' 多相継承において,ベースタイプに基づくポインタ配列から暗黙の 'New[]'/'Delete[]' 演算子を使うコード
'    不適切な動作や予期せぬ動作を回避するためのメンバー手続きを追加します:
'       - 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. 配置 New[] 演算子を継承ポリモーフィズム (サブタイプ ポリモーフィズム) で使う
配列内の基本型ポインターのコレクション (基本型 Ptr 配列)。各ポインターは、同じ派生型の複数の派生オブジェクトをアドレス指定できるバッファー ポインターです。
メモリの割り当て/割り当て解除は、別のプロセス (割り当て/割り当て解除) によって行われます。

前の例から
メモリ割り当てとオブジェクト構築の場合:
変更前:
Dim As Animal Ptr pa(0 To ...) = {New Cat[2], New Dog[2]}
変更後:
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 I As Integer = LBound(pa) To UBound(pa)
'    Delete[] pa(I)  '' does not work
    pa(I)->DeleteSB_launcher()
Next I
変更後:
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)
警告 継承ポリモーフィズム (サブタイプ ポリモーフィズム) の文脈で仮想演算子 '[]' を使用する場合:
- バッファー内の n 番目の派生オブジェクトの正しいアドレスを計算するには、基本型ポインター'p'から、仮想演算子 '[]' を使用して返すことができます (使用: 'Return (@This)[n]' ) この n 番目の派生オブジェクトへの参照。
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. 多重定義 New と Delete 演算子を継承ポリモーフィズム (サブタイプ ポリモーフィズム) で使う
配列内の基本型ポインターのコレクション (基本型 Ptr 配列)。各ポインターは、任意の派生型の単一の派生オブジェクトを、アドレス指定します。
New/Delete 演算子は多重定義されています。

1 つの予期しない動作が発生しました
静的な Delete 命令文では、オブジェクトの実際の実行時型を取得できません:
- このため、代わりに基本型の Delete 演算子が呼び出されるため、派生型の多重定義 Delete 演算子のユーザー コードは実行されません。
- 最も安全なのは、基本型の (抽象) を上書きする各派生型の Delete 命令文に仮想ランチャーを使うことです。この場合 (派生型ポインターで Delete 命令文を呼び出すなど)、仮想デストラクタを定義することは必須ではありませんが、引き続き推奨されます。
- 次の例では、呼び出される仮想ランチャーは「Delete_launcher()」です。


' 多相継承文脈で基本型付きポインター配列から、多重定義 'New'/'Delete' 演算子を使うためのコード
'     予期しない動作を回避するためにメンバー手続きを追加しました:
'       - 抽象/仮想 'Delete_launcher()' は、基本型のポインターから派生型の多重定義 Delete 演算子を呼び出します

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. 多重定義 New[]演算子、Delete[]演算子を継承多相性(サブタイプポリモーフィズム)で使う
配列内の基本型ポインターのコレクション (基本型 Ptr 配列)。各ポインターは、同じ派生型の複数の派生オブジェクトをアドレス指定できるバッファー ポインターです。
多重定義された New[]/Delete[] 演算子。

1 つの不適切な動作と 1 つの予期しない動作が発生しました
派生オブジェクトの正しいアドレスの計算は、最初のオブジェクトを除いて、通常 false です (実際のオブジェクト型ではなくポインター型に対応するオブジェクト サイズとして考慮されるため):
- 多重定義 New[]演算子で構築された配列バッファにある派生オブジェクト(派生型に少なくとも 1 つのデータ フィールドが含まれる場合)の正しいアドレスに、ベース型のポインタからアクセスするには、'[]'演算子を多重定義して、ベース型の(抽象)演算子を上書きする仮想'[]'演算子を派生型に持つことが解決策です。
- 多重定義 '[]' 演算子 (暗黙の '[]' 演算子ではない) を適切に呼び出すには、間接参照されたポインター (暗黙の '[]' 演算子を呼び出す(他のモジュールやプロセスとの間でデータを共有する) rough ポインターではない) で呼び出す必要があります。
- 'p' が基底型のポインターの場合、正しい式は '(*p)[n]' であり、'p[n]' ではありません。
- 警告: n 番目の派生オブジェクトの正しいアドレスを計算するために、仮想 '[]' 演算子は、常に配列バッファーの最初のオブジェクトに対応する '*p' 参照で呼び出されます。従って、この演算子を正しく上書きするには、配列バッファの最初のオブジェクトが常に有効な派生オブジェクトである(たとえば破壊されていない)ことを前提とします。
静的な Delete[] 命令文では、オブジェクトの実際の実行時型を取得できません:
- そのため、破棄する各オブジェクトのアドレスの計算(必要な場合)は、明らかに最初のオブジェクトを除いて、一般的に間違っています(オブジェクトのサイズとして、実際のオブジェクト型ではなく、ポインタ型に対応するものを考慮するため)。
- また、派生型の多重定義 Delete[] 演算子のユーザー コードは実行されず、代わりにベース型の Delete[] 演算子が呼び出されます。
- これら 2 つの結果に対する最も安全な方法は、基底型の(抽象的な)Delete[] 演算子を上書きする各派生型に、常に仮想ランチャーを使うことです。
この場合(派生型のポインタで Delete[] 演算子を呼び出す)、仮想デストラクタを定義することは必須ではありませんが、依然として推奨されています。
- 以下の例では、呼び出される仮想ランチャーは「DeleteSB_launcher()」です。


' 多相継承文脈で、基本型付きポインター配列から多重定義 'New[]'/'Delete[]' 演算子を使うコード
'     不適切な動作や予期しない動作を回避するためのメンバー手続きを追加しました:
'       - 基本型ポインタから派生オブジェクトの正しいアドレスにアクセスするための、抽象/仮想 'Operator []()'
'       - 抽象/仮想 'DeleteSB_launcher()' は、基本型ポインターから正しいオブジェクトを破棄し、基本型ポインターから派生型の多重定義 Delete[] 演算子を呼び出します

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.

先頭に戻る



まとめ:
継承多相性(サブタイプ多相性)における New([])/Delete([]) 演算子のあらゆる使用法(単純/配列版、暗黙/オーバーロード)に同時に対応するために、不適当または予期せぬ動作を回避するための3つのメンバー手続きを追加しなければなりません:
- 基本型ポインタから派生オブジェクトの正しいアドレスにアクセスするための抽象/仮想 'Operator []()' 。
- 基本型のポインターから派生型のオーバーロード Delete 演算子を呼び出すための抽象/仮想 'Delete_launcher()' 。
- 基本型ポインターから正しいアドレスにあるオブジェクトを破棄し、基本型ポインターから派生型のオーバーロード Delete[] 演算子を呼び出すための抽象/仮想 'DeleteSB_launcher()' 。

各派生型には、追加されたメンバー 手続きの「仮想」宣言と本体が含まれますが、基本型には「抽象」宣言のみが含まれます:
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).

先頭に戻る



参照:
プログラマーのための案内に戻る
←リンク元に戻る プログラム開発関連に戻る

ページ歴史:2022-12-15 03:13:05
日本語翻訳:WATANABE Makoto、原文著作者:fxm

ホームページのトップに戻る

表示-非営利-継承