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

FreeBASIC ProPgOperatorOverloading

目次→教本→プログラマーのための案内Operator Overloading←オリジナル・サイト

演算子の多重定義 左にメニュー・フレームが表示されていない場合は、ここをクリックして下さい

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

内臓演算子で使える、ユーザ定義型を変える方法

概要
グローバルな演算子
メンバー演算子
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 ) )


演算子 Iif の呼び出しは、手続き呼び出しと同様であることに注意してください。演算子 * (Value of)演算子 / (Divide) の呼び出しは、手続き呼び出しとは、異なります。
上の例では、p は、演算子 * (Value of) の演算対象です。204 は、それぞれ 演算子 / (Divide) の、左側と右側の、演算対象です。

FreeBASIC のすべての演算子は、IntegerSingle のような、標準のデータ型の演算対象を取るように、事前に定義されています。しかし、演算子は、ユーザ定義の型のために、多重定義することができます。
すなわち、オブジェクトの演算対象を受け入れるために、演算子を定義できます。
多重定義できる演算子には、グローバル演算子メンバー演算子の、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 と表示されるはずです。

メンバー演算子
メンバー演算子は、メンバー手続きのように、TypeClass 定義の中で宣言されます。メンバー演算子は、型変換と、代入の演算子で、以下のものです。
Let (代入), Cast (演算子), += (加えて代入), -= (引いて代入), *= (掛けて代入), /= (割って代入), \= (整数除算して代入), ^= (べき乗して代入), &= (連結して代入), Mod= (剰余値を代入), Shl= (左シフトして代入), Shr= (右シフトして代入), And= (論理積値を代入), Or= (論理和値を代入), Xor= (排他的論理和値を代入), Imp= (論理包含値を代入), Eqv= (論理等価値を代入)

メンバー演算子を宣言するときは、Declare演算子 キーワードの後ろに、演算子記号と、演算子記号のパラメータ・リストを付けます。
メンバー手続きのように、メンバー演算子は、TypeClass 定義の、外で定義されます。そして、識別名の前に、TypeClass 名を置きます。

下の例は、メンバー演算子 Cast (演算子)*= (掛けて代入) を、ユーザ定義の型のオブジェクトのために、多重定義します。

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

プログラマーのための案内に戻る
←リンク元に戻る プログラム開発関連に戻る
ページ歴史:2021-07-11 03:48:21
日本語翻訳:WATANABE Makoto、原文著作者:LaananFisher

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

表示-非営利-継承