ポインターの代わりに
参照 と
利用 を宣言する構文。
序文:
参照の宣言に使われるさまざまな構文は、すべて 'Byref' キーワードを使います。
ポインターは変数なので、その内容を変更することができ、同じポインターを使って異なる変数に連続してアクセスできます。
ただし、参照とそれが指定するオブジェクトとの関連付けは、宣言時に固定されます。
'Byref' キーワードは、参照によって宣言されている変数を示します。 3つの異なる文脈で使われます:
- 手続きの署名で、参照によって引数を渡す(byref パラメーター)。
- 関数の署名で、参照によって呼び出し元に変数を返す(byref return)。
- コードの本文で、参照変数(byref 変数)を定義します。
目次
1. 参照による手続きへのパラメーターの受け渡し(byref パラメーター)
- 宣言の構文:
{Sub|Function} procedure_name (Byref parameter As [Const] datatype, ...
手続きのパラメータリストで使われる場合、
Byref キーワードは、引数が値ではなく参照によって渡されることを示します。
その結果、呼び出された手続きの引数に加えられた変更は、呼び出しの本文に反映されます。
手続きが、送信されたオブジェクトを変更する必要がない場合、または変更してはならない場合は、
Const 修飾子を宣言(
データ型の宣言の前)で使って、渡されたオブジェクトが任意の場所で変更されていないことを、コンパイラが手続きの本体でチェックできるようにします (変更されると、コンパイラ・エラーメッセージが表示されます)。
- 参照によってパラメーターを渡す、完全な構文の例:
Declare Sub passbyref (ByRef ref As Double, ByVal value As Double) '' declaration for passing by reference
Dim As Double X = 0
Print X
passbyref(X, 1.23)
Print X
Sleep
Sub passbyref (ByRef ref As Double, ByVal value As Double) '' declaration for passing by reference
ref = value
End Sub
出力:
注: 引数の項で、Byval キーワードをポインター名の前に指定すると、ポインターは(先に間接参照しなくても)直接 Byval 手続きパラメーターに渡すことができます。
2. 関数から参照で変数を返す(byref return)
- 宣言の構文:
Function function_name (...) Byref As [Const] datatype
関数の戻り値の型で使われる場合、
Byref キーワードは、変数が値ではなく参照によって返されることを示します。
その結果、呼び出し元は関数によって返される変数を変更でき、変更は関数が処理する変数の状態に反映されます。
呼び出し側が送信されたオブジェクトを変更する必要がない場合、または変更してはならない場合、
Const 修飾子を宣言(
データ型の宣言の前)で使って、コンパイラが、返されたオブジェクトが任意の場所で変更されていないことを、呼び出し側の本体でチェックできるようにします (変更されると、コンパイラ・エラーメッセージが表示されます)。
演算子(メンバーまたはグローバル)を関数として使う場合、同様の構文を使って、参照によって結果を返すことができます。
- 参照によって変数を返す、完全な構文例:
Declare Function returnbyref () ByRef As Double '' declaration for returning by reference
Print returnbyref()
returnbyref() = 4.56
Print returnbyref()
Sleep
Function returnbyref () ByRef As Double '' declaration for returning by reference
Static As Double X = 0
Return X
End Function
出力:
引数リストは、空の場合でも常に括弧で囲む必要があります。
特定の構文:
'=' 記号を使った代入式の左側では、解析のあいまいさを解決するために、関数が単一の引数を呼び出すとき、関数の結果(参照によって返される)を括弧で囲む必要があります。
fbc バージョン 0.90 から、初期化子と同様の '=' の代わりに、代入に '=>' を使うことができるので、(括弧なしの)解析のあいまいさを回避できます。
Declare Function transitbyref( ByRef _s As String ) ByRef As String
Dim As String s
s = "abcd"
Print s
'' the enclosing parentheses are required here.
( transitbyref( s ) ) = transitbyref( s ) & "efgh"
Print s
'' the enclosing parentheses are not required here.
transitbyref( s ) => transitbyref( s ) & "ijkl"
Print s
Sleep
Function transitbyref( ByRef _s As String ) ByRef As String
'' This var-len string will transit by reference (input and output), no copy will be created.
Return _s
End Function
出力:
abcd
abcdefgh
abcdefghijkl
注: identifier= や
Function= や
Return 命令文で、
Byval キーワードがポインター名の前に指定されている場合、
Byref 関数の戻りに対して、ポインターを直接(最初に間接参照しなくても)返すことができます。
3. コード内で参照変数を定義(byref 変数)
- 宣言の構文:
又は
ポインターとは異なり、参照変数は、宣言するとすぐに初期化子を使って割り当てる必要があります。
データ型は、変数と同じ型、または互換性のある型(たとえば、継承の場合はその Base 型からの型)でなければなりません:
- 2つの型が同一(または
Var で2番目の構文を使う)の場合にのみ、参照変数は変数の別名と見なすことができます。
このような参照変数を使って、元の変数で実行できる操作と同じ操作を実行できます。
- それ以外(型は互換性があるが同一ではない)の場合、元の変数と同じ操作をすべて行うことはできません:
たとえば、派生型オブジェクトを参照する基本型参照変数は、派生型オブジェクトを参照する基本型ポインタと同様に、仮想メソッドが呼び出されたときにポリモーフィズムをアクティブにすることができます。
同じ型の間接参照されたポインタでできるのと同じ参照変数を介して、同じ操作を行うことができます(ただし、派生型インスタンスを直接使う場合と同じ操作ではありません)。
コードが参照されるオブジェクトを変更する必要がないか、または変更する必要がない場合、
Const 修飾子を宣言で(最初の構文の
data_type の宣言の前に)使用して、オブジェクトが、参照変数を介して、任意の場所で変更されていないことをコンパイラがチェックできるようにします(変更されると、コンパイラ・エラーメッセージが表示されます)。
参照の寿命と、参照されるオブジェクトの寿命との間に、相互作用はありません(ポインターと同様:オブジェクトを破棄しても、そのポインターは破棄されません)。
一度作成されると、それぞれが自分の人生を独立して生きます。
- 参照変数をコードで定義する、完全な構文例:
Dim As Double X = 0
Dim ByRef As Double refX = X '' declaration for defining a reference
Print X
refX = 7.89
Print X
Sleep
出力:
4. 参照とポインターの比較例
- 2つの整数変数から、大きい方の変数を返す関数:
- ポインターを使う(ポインター変数の受け渡し):
Function maxPtr (ByVal p1 As Integer Ptr, ByVal p2 As Integer Ptr) As Integer Ptr
If *p1 > *p2 Then
Return p1
Else
Return p2
End If
End Function
Dim As Integer i1 = 1, i2 = 2
Print i1, i2
*maxPtr(@i1, @i2) = 3
Print i1, i2
Sleep
出力:
- 参照を使う(参照変数の受け渡し):
Function maxRef (ByRef r1 As Integer, ByRef r2 As Integer) ByRef As Integer
If r1 > r2 Then
Return r1
Else
Return r2
End If
End Function
Dim As Integer i1 = 1, i2 = 2
Print i1, i2
maxRef(i1, i2) = 3
Print i1, i2
Sleep
出力:
- サブルーチンを上書きし、共変を返す関数を上書きする継承構造:
- オブジェクトへのポインターを使う:
Type myBase Extends Object
Declare Virtual Function clone () As myBase Ptr
Declare Virtual Sub Destroy ()
End Type
Function myBase.clone () As myBase Ptr
Dim As myBase Ptr pp = New myBase(This)
Print "myBase.clone() As myBase Ptr", pp
Function = pp
End Function
Sub myBase.Destroy ()
Print "myBase.Destroy()", , @This
Delete @This
End Sub
Type myDerived Extends myBase
Declare Function clone () As myDerived Ptr override '' overriding member function with covariant return
Declare Sub Destroy () override '' overriding member subroutine
End Type
Function myDerived.clone () As myDerived Ptr '' overriding member function with covariant return
Dim As myDerived Ptr pc = New myDerived(This)
Print "myDerived.clone() As myDerived Ptr", pc
Function = pc
End Function
Sub myDerived.Destroy () '' overriding member subroutine
Print "myDerived.Destroy()", , @This
Delete @This
End Sub
Dim As myDerived c
Dim As myBase Ptr ppc = @c '' base type pointer to derived object c
Dim As myDerived Ptr pcc = @c '' derived type pointer to derived object c
Dim As myBase Ptr ppc1 = ppc->clone() '' base type pointer to clone of object c
' (through its base type pointer and polymorphism)
Dim As myDerived Ptr pcc1 = pcc->clone() '' derived type pointer to derived object c
' (through its derived type pointer and covariance of return value)
Print
ppc1->Destroy() '' using base type pointer and polymorphism
pcc1->Destroy() '' using derived type pointer
Sleep
出力例:
myDerived.clone() As myDerived Ptr 4663904
myDerived.clone() As myDerived Ptr 4663952
myDerived.Destroy() 4663904
myDerived.Destroy() 4663952
- オブジェクトへの参照を使う:
Type myBase Extends Object
Declare Virtual Function clone () ByRef As myBase
Declare Virtual Sub Destroy ()
End Type
Function myBase.clone () ByRef As myBase
Dim As myBase Ptr pp = New myBase(This)
Print "myBase.clone() Byref As myBase", pp
Function = *pp
End Function
Sub myBase.Destroy ()
Print "myBase.Destroy()", , @This
Delete @This
End Sub
Type myDerived Extends myBase
Declare Function clone () ByRef As myDerived override '' overriding member function with covariant return
Declare Sub Destroy () override '' overriding member subroutine
End Type
Function myDerived.clone () ByRef As myDerived '' overriding member function with covariant return
Dim As myDerived Ptr pc = New myDerived(This)
Print "myDerived.clone() Byref As myDerived", pc
Function = *pc
End Function
Sub myDerived.Destroy () '' overriding member subroutine
Print "myDerived.Destroy()", , @This
Delete @This
End Sub
Dim As myDerived c
Dim ByRef As myBase rpc = c '' base type reference to derived object c
Dim ByRef As myDerived rcc = c '' derived type reference to derived object c
Dim ByRef As myBase rpc1 = rpc.clone() '' base type reference to clone of object c
' (through its base type reference and polymorphism)
Dim ByRef As myDerived rcc1 = rcc.clone() '' derived type reference to derived object c
' (through its derived type reference and covariance of return value)
Print
rpc1.Destroy() '' using base typpe reference and polymorphism
rcc1.Destroy() '' using derived type reference
Sleep
出力例:
myDerived.clone() Byref As myDerived 9775712
myDerived.clone() Byref As myDerived 9775760
myDerived.Destroy() 9775712
myDerived.Destroy() 9775760
5. FreeBASIC で許可されている追加構文を使う、参照の使用法のハッキング
FB で、参照は、変数のアドレスを保持する、内部ポインターを介して、覆い(頭巾)の下で実装されます。
現在のところ、この内部ポインターへのアクセスは、ユーザーに対して、読み込み時に、参照変数に対しては書き込み時に、許可されています(他の多くの言語とは異なります):
- したがって、参照変数のシンボル名に適用される '@' 演算子を使って、参照される変数のアドレス(内部ポインターの値)を取得できます:
- さらに、次のようにすると、参照変数を、互換性のある別の変数を参照するように再割り当て(内部ポインタの値を変更することにより)できます:
- 内部ポインタのアドレスも取得できます:
internal_pointer_address = @@ref
注:
- 参照変数は、"null" 参照に再初期化することもできます:
- 参照変数は、"null" 参照として、直接宣言することもできます:
したがって、常に同じ参照シンボル名を使うことで、参照の純粋な構文と、その内部ポインターの構文を、混在させることができます。
- 参照シンボル名のハッキング例:
Declare Function resizeZstring (ByRef refZstring As ZString, ByVal length As Integer) ByRef As ZString
Declare Sub prntZstring (ByRef refZstring As ZString)
Dim ByRef As ZString refZ = *CPtr(ZString Ptr, 0) '' "null" reference declaration
Const cz1 = "FB"
@refZ = @(resizeZstring(refZ, Len(cz1))) '' reference (re-)inititialization
refZ = cz1
prntZstring(refZ)
Const cz2 = "FreeBASIC"
@refZ = @(resizeZstring(refZ, Len(cz2))) '' reference re-inititialization
refZ = cz2
prntZstring(refZ)
Const cz3 = "FreeBASIC 1.06.0"
@refZ = @(resizeZstring(refZ, Len(cz3))) '' reference re-inititialization
refZ = cz3
prntZstring(refZ)
Const cz4 = ""
@refZ = @(resizeZstring(refZ, Len(cz4))) '' reference re-inititialization to "null" reference
refZ = cz4
prntZstring(refZ)
Sleep
Function resizeZstring (ByRef refZstring As ZString, ByVal length As Integer) ByRef As ZString
If length > 0 Then
If @refZstring = 0 Then
Print "Zstring memory buffer allocation"
Else
Print "Zstring memory buffer re-allocation"
End If
length += 1
Else
Print "Zstring memory buffer de-allocation"
End If
' Return *Cptr(Zstring Ptr, Reallocate(@refZstring, length * Sizeof(Zstring)))
' '' Using the "Return Byval ..." syntax allows to avoid casting + dereferencing as above
Return ByVal Reallocate(@refZstring, length * SizeOf(ZString))
End Function
Sub prntZstring (ByRef refZstring As ZString)
Print " " & @refZstring, "'" & refZstring & "'"
Print
End Sub
出力例:
Zstring memory buffer allocation
9513600 'FB'
Zstring memory buffer re-allocation
9513600 'FreeBASIC'
Zstring memory buffer re-allocation
9513600 'FreeBASIC 1.06.0'
Zstring memory buffer de-allocation
0 ''
参照