内臓演算子で使える、ユーザ定義型を変える方法
概要
グローバルな演算子
メンバー演算子
Special Cases of Operators: '.' (Member access), '@' (Address of), '->' (Pointer to member access), and '*' (Value of)
概要
単純にいえば、演算子は、手続きです。そして、演算子の引数は、
演算対象 operand と呼ばれます。
1つの演算対象 (
演算子 Not) を取る演算子は、
単項演算子と呼ばれます。二つの演算対象 (
演算子 +) を取る演算子は、
二項演算子と呼ばれます。そして、3つの演算対象 (
演算子 Iif) を取る演算子は、
三項演算子と呼ばれます。
ほとんどの演算子は、手続きのようには、呼ばれません。
代わりに、演算子の演算子記号は、演算子の演算対象の次に、置かれます。
単項演算子では、単項演算子の唯一の演算対象は、演算子記号の右に置かれます。
二項演算子では、二項演算子の演算対象(左側の演算対象と、右側の演算対象と呼ばれる)は、演算子記号の左右に置かれます。
FreeBASIC には、ひとつの三項演算子、
演算子 Iif、があります。
演算子 Iif は、手続きのように呼ばれます。演算対象は、括弧で囲んで、コンマで区切ります。
例えば、下のコードは、ポインタが有効かどうかを判定するために、
演算子 Iif を呼んでいます。
条件が合致すれば、ポインタを逆参照する、
演算子 * (Value of) が呼ばれます。条件が合致しなければ、
演算子 / (Divide) が呼ばれて、20 を 4 で割った値を設定します。
Dim i As Integer = 420
Dim p As Integer Ptr = @i
Dim result As Integer = IIf
( p, *p, CInt( 20 / 4 ) )
FreeBASIC のすべての演算子は、
Integer と
Single のような、標準のデータ型の演算対象を取るように、事前に定義されています。しかし、演算子は、ユーザ定義の型のために、多重定義することができます。
すなわち、オブジェクトの演算対象を受け入れるために、演算子を定義できます。
多重定義できる演算子には、
グローバル演算子と
メンバー演算子の、2 つの型があります。
グローバルな演算子
グローバルな演算子は、モジュールレベル範囲(グローバルに)で宣言されるものです。
これらは、次の演算子です。
- (否定),
Not (否定(補数)),
-> (メンバーアクセスへのポインタ),
* (の値),
+ (加算),
- (減算),
* (乗算),
/ (除算),
\ (整数除算),
& (文字列連結),
Mod (剰余),
Shl (左にシフト),
Shr (右にシフト),
And (論理積),
Or (論理和),
Xor (排他的論理和),
Imp (包含),
Eqv (同値),
^ (べき乗),
= (同等),
<> (等しくない),
< (少なり),
> (大なり),
<= (以下),
>= (以上),
Abs,
Sgn,
Fix,
Frac,
Int,
Exp,
Log,
Sin,
Asin,
Cos,
Acos,
Tan,
Atan,
Len,
Sqr.
あつらえのグローバル演算子を宣言するのは、手続きを宣言するのと、同じです。
Declare キーワードは、
演算子 キーワードと共に、使います。
算子記号は、その後ろに、括弧で囲まれた、コンマで区切ったパラメータのリストが、続けて置かれます。括弧は、演算対象が、演算子に渡されることを表します。
手続きと違って、演算子は、デフォルトで多重定義できます。このため、あつらえの演算子を宣言するときに、
Overload キーワードは必要ありません。
演算子のパラメータのうち、少なくとも1つはユーザー定義型でなければなりません(結局のところ、組み込み型のパラメータを持つ演算子はすでに定義されています)。
下の例は、ユーザ定義された型の演算対象を受け取るために、グローバルな演算子、
- (減算) と
* (乗算) を宣言します。
Type Rational
As Integer numerator, denominator
End Type
Operator - (ByRef rhs As Rational) As Rational
Return Type
(-rhs.numerator, rhs.denominator)
End Operator
Operator * (ByRef lhs As Rational, ByRef rhs As Rational) As Rational
Return Type(lhs.numerator * rhs.numerator, _
lhs.denominator * rhs.denominator)
End Operator
Dim As Rational r1 = (2, 3), r2 =
(3, 4)
Dim As Rational r3 = -
(r1 * r2)
Print r3.numerator & "/" & r3.denominator
Sleep
Here the global operators are defined for type Rational, and are used in the initialization expression for r3.
ここで、グローバルな演算子は、型 Rational として定義されます。そして、この演算子は、r3 を初期化する式に使われます。
上のコードを実行すると、-6/12 と表示されるはずです。
メンバー演算子
メンバー演算子は、メンバー手続きのように、
Type や
Class 定義の中で宣言されます。メンバー演算子は、型変換と、代入の演算子で、以下のものです。
Let
(代入),
Cast (演算子),
+=
(加えて代入),
-=
(引いて代入),
*=
(掛けて代入),
/=
(割って代入),
\=
(整数除算して代入),
^=
(べき乗して代入),
&=
(連結して代入),
Mod=
(剰余値を代入),
Shl=
(左シフトして代入),
Shr=
(右シフトして代入),
And=
(論理積値を代入),
Or=
(論理和値を代入),
Xor=
(排他的論理和値を代入),
Imp= (論理包含値を代入),
Eqv= (論理等価値を代入)
メンバー演算子を宣言するときは、
Declare と
演算子 キーワードの後ろに、演算子記号と、演算子記号のパラメータ・リストを付けます。
メンバー手続きのように、メンバー演算子は、
Type や
Class 定義の、外で定義されます。そして、識別名の前に、
Type か
Class 名を置きます。
Type Rational
As Integer numerator, denominator
Declare Operator Cast () As Double
Declare Operator Cast () As String
Declare Operator *=
(ByRef rhs As Rational)
End Type
Operator Rational.cast () As Double
Return numerator / denominator
End Operator
Operator Rational.cast () As String
Return numerator & "/" & denominator
End Operator
Operator Rational.*=
(ByRef rhs As Rational)
numerator *= rhs.numerator
denominator *= rhs.denominator
End Operator
Dim As Rational r1 = (2, 3), r2 =
(3, 4)
r1 *= r2
Dim As Double d = r1
Print r1, d
Sleep
メンバー演算子
Cast (型変換) が、二度宣言されているのに、注意してください。一つは、
Double への変換のため、もう一つは、
String への変換のためです。
This is the only operator (or procedure) that can be declared multiple times when only the return type differs.
これは、戻りの型が異なるときだけに、複数回、宣言できる、唯一の演算子(または、手続き)です。
コンパイラは、オブジェクトがどう使われるかに基づいて、どの型変換の多重定義を呼んだらよいかを決めます。
(オブジェクトの使われ方として、
Double d の初期化では、
Rational.Cast as double が呼ばれます。そして、
Print 命令文では、
Rational.Cast as string が、代わりに使われます)
演算子の特殊なケース: '.' (メンバーアクセス), '@' (のアドレス), '->' (メンバーアクセスへのポインタ), '*' (の値)
- 多重定義
演算子 . (メンバーアクセス)
演算子 '.' (メンバーアクセス) は多重定義できません。
- 多重定義
演算子 @ (のアドレス)
演算子 @ (のアドレス) は、変数のアドレスにアクセスするために使われます。
オブジェクトに対してこの演算子を多重定義することにはあまり意味がなく、また、多重定義すると、そのアドレスにアクセスできなくなります。
- 多重定義
演算子 -> (メンバーアクセスへのポインタ) と
演算子 * (の値)
演算子 -> (メンバーアクセスへのポインタ) は,このインスタンスへのポインタを介して、オブジェクト (インスタンス) の任意のメンバにアクセスするために使われます。
演算子 * (の値) は,この変数へのポインタを介して、変数にアクセスするために使います。
通常は、これらの演算子の被演算子(演算対象)はポインタでなければなりません:
Declare Operator -> ( ByRef lhs As T Ptr ) ByRef As U
Declare Operator * ( ByRef rhs As T Ptr ) ByRef As T
これらの演算子を多重定義すると、ポインターのラッパークラスを作成し、ポインターそのもののように振る舞うことができます:
Declare Operator -> ( ByRef lhs As wrapperClass ) ByRef As U
Declare Operator * ( ByRef rhs As wrapperClass ) ByRef As U
このラッパーは、次のように(メンバーにアクセスするために)使えます:
wrapper->member
instead of:
wrapper.realPointer->member
and:
(*wrapper).member
instead of:
(*wrapper.realPointer).member
演算子 -> (メンバーアクセスへのポインタ) を多重定義する特殊なケースを明確にする:
演算子 -> (メンバーアクセスへのポインタ) は、多重定義に関して、他の演算子とは異なる動作をします:
- 演算子は,多重定義された手続きのヘッダで示されたユーザデータ型だけを返すのではありません。
- このユーザデータ型の後に暗黙のうちに 演算子 . (メンバーアクセス) を続けて返します。
演算子 -> (メンバーアクセスへのポインタ) は、主に
演算子 * (の値) と組み合わせて、「スマートポインター」を実装するためによく使われます。
-スマートポインタを使う
スマートポインタを使うと、
New で作成された動的な参照を自動的に管理することができます(各参照は、そのスマートポインタがスコープ外になると自動的に破棄されます)。これらの参照のコピーを作成する必要はありません。
スマートポインターとは何かを思い出します:
- スマートポインターとは、ポインターのように振る舞いながら、ポインター以上の機能を持つオブジェクトのことです。
- このオブジェクトは、ポインターとしての柔軟性と、オブジェクトとしての利点(構築子や解体子が自動的に呼び出されるなど)を併せ持っています。
- そのため、スマートポインタの解体子は、このオブジェクトがスコープ外に出たときに自動的に呼び出され、ユーザーポインタを削除します。
スマート・ポインターはポインターのように動作しなければならないので、ポインターと同じインターフェイスをサポートしなければなりません。
そのため、以下の操作をサポートしなければなりません:
- 逆参照 (演算子 * (の値))
- 間接参照 (演算子 -> (メンバアクセスへのポインタ))
演算子 * (の値) と
演算子 -> (メンバアクセスへのポインタ) は,参照を返さなければなりません (戻り値の型の宣言で
Byref As ..... を使うことで)。
インターフェイスを持つスマートポインタ(UDTへの)の例:
- public default-constructor
- public copy-constructor
- public destructor
- private UDT pointer and public
operator cast (Cast) to access it in read only mode
- private operator 'let' to disallow assignment not implemented here (to avoid copying the pointers values only)
-
operator * (Value of) and
operator -> (pointer to member access)
Type UDT
Declare Constructor ()
Declare Destructor ()
Dim As String s = "object #0"
End Type
Constructor UDT ()
Print " UDT construction "; @This
End Constructor
Destructor UDT ()
Print " UDT destruction "; @This
End Destructor
Type SmartPointer
Public:
Declare Constructor () '' to construct smart pointer (and UDT object)
Declare Constructor (ByRef rhs As SmartPointer) '' to copy construct smart pointer
Declare Operator Cast () As UDT Ptr '' to cast private UDT pointer (for read only)
Declare Destructor () '' to destroy smart pointer (and UDT object)
Private:
Dim As UDT Ptr p '' private UDT pointer
Declare Operator Let (ByRef rhs As SmartPointer) '' to disallow assignment (to avoid copy of real pointers)
End Type
Constructor SmartPointer ()
Print "SmartPointer construction "; @This
This.p = New UDT
End Constructor
Constructor SmartPointer (ByRef rhs As SmartPointer)
Print "SmartPointer copy-construction "; @This; " from "; @rhs
This.p = New UDT
*This.p = *rhs.p
End Constructor
Operator SmartPointer.Cast () As UDT Ptr
Return This.p
End Operator
Destructor SmartPointer ()
Print "SmartPointer destruction "; @This
Delete This.p
End Destructor
Operator * (ByRef sp As SmartPointer) ByRef As UDT '' overloaded operator '*'
Print "SmartPointer operator '*'"
Return *Cast(UDT Ptr, sp) '' (returning byref)
End Operator '' to behave as pointer
Operator -> (ByRef sp As SmartPointer) ByRef As UDT '' overloaded operator '->'
Print "SmartPointer operator '->'"
Return *Cast(UDT Ptr, sp) '' (returning byref)
End Operator '' to behave as pointer
Scope
Dim sp1 As SmartPointer
Print "'" & sp1->s & "'"
sp1->s = "object #1"
Print "'" & sp1->s & "'"
Print
Dim sp2 As SmartPointer = sp1
Print "'" & (*sp2).s & "'"
(*sp2).s = "object #2"
Print "'" & (*sp2).s & "'"
Print
Dim sp3 As SmartPointer = sp1
Print "'" & sp3->s & "'"
*sp3 = *sp2
Print "'" & sp3->s & "'"
sp3->s = "object #3"
Print "'" & sp3->s & "'"
Print
End Scope
Sleep
出力例:
SmartPointer construction 1703576
UDT construction 10693312
SmartPointer operator '->'
'object #0'
SmartPointer operator '->'
SmartPointer operator '->'
'object #1'
SmartPointer copy-construction 1703524 from 1703576
UDT construction 10693384
SmartPointer operator '*'
'object #1'
SmartPointer operator '*'
SmartPointer operator '*'
'object #2'
SmartPointer copy-construction 1703472 from 1703576
UDT construction 10693456
SmartPointer operator '->'
'object #1'
SmartPointer operator '*'
SmartPointer operator '*'
SmartPointer operator '->'
'object #2'
SmartPointer operator '->'
SmartPointer operator '->'
'object #3'
SmartPointer destruction 1703472
UDT destruction 10693456
SmartPointer destruction 1703524
UDT destruction 10693384
SmartPointer destruction 1703576
UDT destruction 10693312
拡張インターフェイスを備えた、任意の UDT(または任意の定義済み型)用の、拡張スマート・ポインター型マクロの例:
- public constructor
- public reference counter in read only mode
- public destructor
- private UDT pointer and 2 public operators
cast to access it in read only mode (numeric value and string value)
- private default-constructor to disallow self construction
- private copy-constructor to disallow cloning
- private operator
let to disallow assignment
-
operator * (Value of) and
operator -> (pointer to member access)
#Macro Define_SmartPointer (_UDTname_)
Type SmartPointer_##_UDTname_
Public:
Declare Constructor (ByVal rhs As _UDTname_ Ptr) '' to construct smart pointer
' '' from _UDTname_ pointer,
' '' with reference counter increment
Declare Static Function returnCount () As Integer '' to return reference counter value
Declare Operator Cast () As _UDTname_ Ptr '' to cast private _UDTname_ pointer
' '' to _UDTname_ pointer (read only)
Declare Operator Cast () As String '' to cast private _UDTname_ pointer
' '' to string (read only)
Declare Destructor () '' to destroy smart pointer
' '' and _UDTname_ object
' '' with reference counter decrement
Private:
Dim As _UDTname_ Ptr p '' private _UDTname_ pointer
Static As Integer Count '' private reference counter
Declare Constructor () '' to disallow default-construction
Declare Constructor (ByRef rhs As SmartPointer_##_UDTname_) '' to disallow copy-construction
Declare Operator Let (ByRef rhs As SmartPointer_##_UDTname_) '' to disallow copy-assignment
End Type
Dim As Integer SmartPointer_##_UDTname_.Count = 0
Constructor SmartPointer_##_UDTname_ (ByVal rhs As _UDTname_ Ptr)
If rhs <> 0 Then
This.p = rhs
SmartPointer_##_UDTname_.count += 1
End If
End Constructor
Static Function SmartPointer_##_UDTname_.returnCount () As Integer
Return SmartPointer_##_UDTname_.count
End Function
Operator SmartPointer_##_UDTname_.Cast () As _UDTname_ Ptr
Return This.p
End Operator
Operator SmartPointer_##_UDTname_.Cast () As String
Return Str(This.p)
End Operator
Destructor SmartPointer_##_UDTname_ ()
If This.p <> 0 Then
Delete This.p
SmartPointer_##_UDTname_.count -= 1
This.p = 0
End If
End Destructor
Operator * (ByRef sp As SmartPointer_##_UDTname_) ByRef As _UDTname_ '' operator '*' (return byref)
' '' to behave as pointer
Return ByVal sp '' 'Return *sp' would induce an infinite loop
End Operator
Operator -> (ByRef sp As SmartPointer_##_UDTname_) ByRef As _UDTname_ '' operator '->' (return byref)
' '' to behave as pointer
Return ByVal sp
End Operator
#Endmacro
'--------------------------------------------------------------------------------------------------------
' Example using all eight keywords of inheritance:
' 'Extends', 'Base.', 'Base()', 'Object', 'Is' operator, 'Virtual', 'Abstract', 'Override'
Type root Extends Object ' 'Extends' to activate RTTI by inheritance of predefined Object type
Public:
Declare Function ObjectHierarchy () As String
Declare Function ObjectName () As String
Declare Abstract Function ObjectRealType () As String '' 'Abstract' declares function without local body
' '' which must be overridden
Declare Virtual Destructor () '' 'Virtual' declares destructor
Protected:
Declare Constructor () '' to avoid default-construction from outside Types
Declare Constructor (ByRef _name As String = "") '' to avoid construction from outside Types
Declare Constructor (ByRef rhs As root) '' to avoid copy-construction from outside Types
Declare Operator Let (ByRef rhs As root) '' to avoid copy-assignment from outside Types
Private:
Dim Name As String
End Type '' derived type may be member data empty
Constructor root () '' only to avoid compile error (due to inheritance)
End Constructor
Constructor root (ByRef _name As String = "") '' only to avoid compile error (due to inheritance)
This.Name = _name
Print "root constructor:", This.Name
End Constructor
Function root.ObjectHierarchy () As String
Return "Object(forRTTI) <- root"
End Function
Function root.ObjectName () As String
Return This.Name
End Function
Virtual Destructor root ()
Print "root destructor:", This.Name
End Destructor
Operator root.Let (ByRef rhs As root) '' only to avoid compile error (due to onheritance)
End Operator
Type animal Extends root '' 'Extends' to inherit of root
Declare Constructor (ByRef _name As String = "")
Declare Function ObjectHierarchy () As String
Declare Virtual Function ObjectRealType () As String Override '' 'Virtual' declares function with local
' '' body which can be overridden
' '' 'Override' to check if the function is
' '' well an override
Declare virtual Destructor () Override '' 'Virtual' declares destructor with local body
' '' 'Override' to check if the destructor is well an override
End Type
Constructor animal (ByRef _name As String = "")
Base(_name) '' 'Base()' allows to call parent constructor
Print " animal constructor:", This.ObjectName()
End Constructor
Function animal.ObjectHierarchy () As String
Return Base.ObjectHierarchy & " <- animal" '' 'Base.' allows to access to parent member function
End Function
Virtual Function animal.ObjectRealType () As String
Return "animal"
End Function
Virtual Destructor animal ()
Print " animal destructor:", This.ObjectName()
End Destructor
Type dog Extends animal '' 'Extends' to inherit of animal
Declare Constructor (ByRef _name As String = "")
Declare Function ObjectHierarchy () As String
Declare Function ObjectRealType () As String Override '' 'Override' to check if the function is well an
' '' override
Declare Destructor () Override '' 'Override' to check if the destructor is well an override
End Type '' derived type may be member data empty
Constructor dog (ByRef _name As String = "")
Base(_name) '' 'Base()' allows to call parent constructor
Print " dog constructor:", This.ObjectName()
End Constructor
Function dog.ObjectHierarchy () As String
Return Base.ObjectHierarchy & " <- dog" '' 'Base.' allows to access to parent member function
End Function
Function dog.ObjectRealType () As String
Return "dog"
End Function
Destructor dog ()
Print " dog destructor:", This.ObjectName()
End Destructor
Type cat Extends animal '' 'Extends' to inherit of animal
Declare Constructor (ByRef _name As String = "")
Declare Function ObjectHierarchy () As String
Declare Function ObjectRealType () As String Override '' 'Override' to check if the function is well an
' '' override
Declare Destructor () Override '' 'Override' to check if the destructor is well an override
End Type '' derived type may be member data empty
Constructor cat (ByRef _name As String = "")
Base(_name) '' 'Base()' allows to call parent constructor
Print " cat constructor:", This.ObjectName()
End Constructor
Function cat.ObjectHierarchy () As String
Return Base.ObjectHierarchy & " <- cat" '' 'Base.' allows to access to parent member function
End Function
Function cat.ObjectRealType () As String
Return "cat"
End Function
Destructor cat ()
Print " cat destructor:", This.ObjectName()
End Destructor
Sub PrintInfo (ByVal p As root Ptr) '' parameter is a 'root Ptr' or compatible (smart pointer)
Print " " & p->ObjectName, " " & p->ObjectRealType, " ";
If *p Is dog Then '' 'Is' allows to check compatibility with type symbol
Print Cast(dog Ptr, p)->ObjectHierarchy
ElseIf *p Is cat Then '' 'Is' allows to check compatibility with type symbol
Print Cast(cat Ptr, p)->ObjectHierarchy
ElseIf *p Is animal Then '' 'Is' allows to check compatibility with type symbol
Print Cast(animal Ptr, p)->ObjectHierarchy
End If
End Sub
Define_SmartPointer(root) '' smart pointer definition
Scope
Print "reference counter value:"; SmartPointer_root.returnCount()
Print
Dim As SmartPointer_root sp(2) = {New animal("Mouse"), New dog("Buddy"), New cat("Tiger")}
Print
Print "reference counter value:"; SmartPointer_root.returnCount()
For I As Integer = 0 To 2
Print " " & sp(I), sp(I)->ObjectName()
Next I
Print
Print "Name:", "Object (real): Hierarchy:"
For I As Integer = 0 To 2
PrintInfo(sp(I))
Next I
Print
End Scope
Print
Print "reference counter value:"; SmartPointer_root.returnCount()
Print
Sleep
出力例:
reference counter value: 0
root constructor: Mouse
animal constructor: Mouse
root constructor: Buddy
animal constructor: Buddy
dog constructor: Buddy
root constructor: Tiger
animal constructor: Tiger
cat constructor: Tiger
reference counter value: 3
11145960 Mouse
11151496 Buddy
11151616 Tiger
Name: Object (real): Hierarchy:
Mouse animal Object(forRTTI) <- root <- animal
Buddy dog Object(forRTTI) <- root <- animal <- dog
Tiger cat Object(forRTTI) <- root <- animal <- cat
cat destructor: Tiger
animal destructor: Tiger
root destructor: Tiger
dog destructor: Buddy
animal destructor: Buddy
root destructor: Buddy
animal destructor: Mouse
root destructor: Mouse
reference counter value: 0