可変長の文字列/配列 を型の
メンバーとして管理する。
序文:
FreeBASIC では、型データ構造は最終的に固定サイズでなければならず、コンパイラはその型のオブジェクトに割り当てるメモリ量を知ることができます。
しかし、型には可変長の文字列や配列のデータメンバを含めることができます。
しかし、文字列/配列のデータは直接型には埋め込まれません。代わりに、型には文字列/配列記述子構造だけが含まれ、FreeBASIC は裏でこれを使って可変長の文字列/配列データを管理します。
型の配列記述子の構造をサイズ調整するために、可変長の配列データメンバーは、配列境界の代わりに常に Any を使って宣言する必要があります。指定された Any の数に基づいて次元数を固定します。可変長の配列データメンバーは、ReDim を含む構文を使って、宣言時にあらかじめサイズを指定することもできます。
可変長配列フィールドは、型で宣言されたときに、擬似オブジェクトとみなされます(可変長の文字列は実オブジェクトです)。
そのため、型自体の暗黙のコピー構築子と暗黙の let 演算子が、このような文字列/配列の[再]サイジングとコピー、またそれらの消去をサポートしています。
コンパイラ・コードによる暗黙の文字列/配列のサイジングとコピー
コンパイラがこのような型(文字列/配列のメンバを持つ)のためにデフォルトのコピー構築子とデフォルトのコピー割り当て演算子を構築するとき、必要に応じて、送信先の文字列/配列のサイズ調整と送信元の文字列/配列からのデータのコピーのためのすべてのコードが含まれます。
例:
Type UDT
Dim As String s
Dim As Integer array(Any)
End Type
Dim As UDT u1, u2
u1.s = "FreeBASIC"
ReDim u1.array(1 To 9)
For I As Integer = LBound(u1.array) To UBound(u1.array)
u1.array(I) = I
Next I
u2 = u1
Print u2.s
For I As Integer = LBound(u2.array) To UBound(u2.array)
Print u2.array(I);
Next I
Print
Print
Dim As UDT u3 = u1
Print u3.s
For I As Integer = LBound(u3.array) To UBound(u3.array)
Print u3.array(I);
Next I
Print
Sleep
出力:
FreeBASIC
1 2 3 4 5 6 7 8 9
FreeBASIC
1 2 3 4 5 6 7 8 9
コンパイラコードによる暗黙の文字列/配列のサイズ調整とコピーが、明示的なコピー構築子とコピー代入演算子によって破壊される
ユーザーが独自のコピー構築子とコピー代入演算子を指定する場合(例えば、追加の複雑な項目メンバを初期化するため)、上記のコンパイラ・コードによる自動的な文字列/配列のサイズ調整とコピーは解除されます。
例:
Type UDT
Dim As String s
Dim As Integer array(Any)
Declare Constructor ()
Declare Constructor (ByRef u As UDT)
Declare Operator Let (ByRef u As UDT)
'user fields
End Type
Constructor UDT ()
'code for user fields in constructor
End Constructor
Constructor UDT (ByRef u As UDT)
'code for user fields in copy-constructor
End Constructor
Operator UDT.Let (ByRef u As UDT)
'code for user fields in copy-assignement operator
End Operator
Dim As UDT u1, u2
u1.s = "FreeBASIC"
ReDim u1.array(1 To 9)
For I As Integer = LBound(u1.array) To UBound(u1.array)
u1.array(I) = I
Next I
u2 = u1
Print u2.s
For I As Integer = LBound(u2.array) To UBound(u2.array)
Print u2.array(I);
Next I
Print
Print
Dim As UDT u3 = u1
Print u3.s
For I As Integer = LBound(u3.array) To UBound(u3.array)
Print u3.array(I);
Next I
Print
Sleep
出力 (空白):
文字列/配列のサイズ調整とコピーは、ユーザーのコピー構築子とコピー代入演算子で明示的に設定される
可変長配列は、例えば暗黙の代入がないため、可変長文字列のように真のオブジェクトとして処理することはできません。
上記の例で言えば、
This.array() = u.array() は許可されませんが、
This.s = u.s は許可されます。
ユーザーは、配列メンバのサイズ設定とコピーを明示的にコーディングする必要があります(配列データのコピーには、実行時間を最適化するために、C言語のランタイム関数
memcpy() が使われます)。
例:
#include "crt/string.bi" '' C run-time header for 'memcpy()'
Type UDT
Dim As String s
Dim As Integer array(Any)
Declare Constructor ()
Declare Constructor (ByRef u As UDT)
Declare Operator Let (ByRef u As UDT)
'user fields
End Type
Constructor UDT ()
'code for user fields in constructor
End Constructor
Constructor UDT (ByRef u As UDT)
This.s = u.s
If UBound(u.array) >= LBound(u.array) Then '' explicit array sizing and copying
ReDim This.array(LBound(u.array) To UBound(u.array))
memcpy(@This.array(LBound(This.array)), @u.array(LBound(u.array)), (UBound(u.array) - LBound(u.array) + 1) * SizeOf(@u.array(LBound(u.array))))
End If
'code for user fields in copy-constructor
End Constructor
Operator UDT.Let (ByRef u As UDT)
If @This <> @u Then '' not self-assignment
This.s = u.s
If UBound(u.array) >= LBound(u.array) Then '' explicit array sizing and copying
ReDim This.array(LBound(u.array) To UBound(u.array))
memcpy(@This.array(LBound(This.array)), @u.array(LBound(u.array)), (UBound(u.array) - LBound(u.array) + 1) * SizeOf(@u.array(LBound(u.array))))
End If
'code for user fields in copy-assignement operator
End If
End Operator
Dim As UDT u1, u2
u1.s = "FreeBASIC"
ReDim u1.array(1 To 9)
For I As Integer = LBound(u1.array) To UBound(u1.array)
u1.array(I) = I
Next I
u2 = u1
Print u2.s
For I As Integer = LBound(u2.array) To UBound(u2.array)
Print u2.array(I);
Next I
Print
Print
Dim As UDT u3 = u1
Print u3.s
For I As Integer = LBound(u3.array) To UBound(u3.array)
Print u3.array(I);
Next I
Print
Sleep
出力:
FreeBASIC
1 2 3 4 5 6 7 8 9
FreeBASIC
1 2 3 4 5 6 7 8 9
可変長の文字列と配列を含む追加のベース型を使う
もう 1つの洗練された方法は、コンパイラによって自動的にコーディングされたこのサイズ変更やコピーを、単に明示的に呼び出すことです。
この場合、メンバー配列の洗練された解決策は、もはや型自体のレベルに置くのではなく、継承された別の特定の型に配置することです(外から見ると、まったく同じです)。これはメンバー文字列には必要ありませんが、これを含めることで、毎回コードを 1行減らすことができます。
例:
Type UDT0
Dim As String s
Dim As Integer array(Any)
End Type
Type UDT Extends UDT0
Declare Constructor ()
Declare Constructor (ByRef u As UDT)
Declare Operator Let (ByRef u As UDT)
'user fields
End Type
Constructor UDT ()
'code for user fields in constructor
End Constructor
Constructor UDT (ByRef u As UDT)
Base(u) '' inherited string copying plus array sizing and copying from Base implicit copy-constructor call
'code for user fields in copy-constructor
End Constructor
Operator UDT.Let (ByRef u As UDT)
Cast(UDT0, This) = u '' inherited string copying plus array sizing and copying from Base implicit copy-assignement operator call
'code for user fields in copy-assignement operator
End Operator
Dim As UDT u1, u2
u1.s = "FreeBASIC"
ReDim u1.array(1 To 9)
For I As Integer = LBound(u1.array) To UBound(u1.array)
u1.array(I) = I
Next I
u2 = u1
Print u2.s
For I As Integer = LBound(u2.array) To UBound(u2.array)
Print u2.array(I);
Next I
Print
Print
Dim As UDT u3 = u1
Print u3.s
For I As Integer = LBound(u3.array) To UBound(u3.array)
Print u3.array(I);
Next I
Print
Sleep
出力:
FreeBASIC
1 2 3 4 5 6 7 8 9
FreeBASIC
1 2 3 4 5 6 7 8 9
参照: