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

FreeBASIC オブジェクトとしての型 入門(1)

目次→教本→いっしょに学ぼうBeginners Guide to Types as Objects←オリジナル・サイト

オブジェクトとしての型 入門(1) 左にメニュー・フレームが表示されていない場合は、ここをクリックして下さい

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


序論

このチュートリアルは、Type に加えられた新機能に関してもう少し知りたいと思っている人々を対象にします。
一般的に 'オブジェクトとしての型'、および 'オブジェクト指向プログラミング(OOP stuff)' と呼ばれる部分です。
これらの新機能を、紹介することを目指しているので、'オブジェクトとしての型'をまだ理解していなくて、学びたいと思っている人々を対象にしています。
FreeBASIC の Type は、C の struct や、パスカルの record のような、集合体データ型です。
下は、典型的な Type の使い方の、短いサンプルです。

Type person_info
  first_name As String
  last_name As String
  house_number As Integer
  street_name As String
  town As String
End Type


上の用法では、Type は、関連するデータに対する、一種のコンテナとして使われています。
この例は、アドレス帳の登録用かもしれません。
しかし、新機能で、Type は、さらに、C++ におけるクラスのように、使用できます。データの単純な項目を、ただ含んでいるだけより、はるかに多くができます。
それは、オブジェクトの考えを表す方法になります。これで、オブジェクト指向プログラミングは、はるかに簡単になります。
オブジェクトとしての型 の新機能を見てみましょう。


Property

私たちは、Property を見ることから始めます。
あなたが property を Type に加えるとき、まるでそれが普通のメンバーであるように、あなたは property にアクセスします。しかし、起こることは、いつものように、変数を、ただ取得したり、セットしたりする代わりに、関数を呼ぶのです。
この例を見てください:

Type bar
  Declare Property x() As Integer
  Declare Property x (ByVal n As Integer)
  p_x As Integer
End Type

Property bar.x() As Integer
  Print "bar.x()"
  Property = p_x
End Property

Property bar.x (ByVal n As Integer)
  Print "bar.x(ByVal n As Integer)"
  p_x = n
End Property

'---

Dim foo As bar

foo.x = 5

Print foo.x
Sleep


私たちの Type に、Property のためのいくつかの宣言を、含めます。
この宣言は、普通の関数宣言と、とてもよく似ています。
一行目は、ゲッタ(参照されたときに呼ばれる)を宣言して、二行目は、セッター(値を変更するときに呼ばれる)を宣言しています。
p_x メンバーは、ただ普通の Integer メンバーです。

次に、私たちは、Property のためにコードを書きます。
やはり、構文は、通常の関数のものと、とてもよく似ています。
ここで、値を返す方法に、注意してください:
Function = 値 の代わりに、Property = 値 とします。
あなたはまた、Return 値 aができます。
また、メンバーを直接 p_x で参照できることに注意してください。
また、キーワード this を使うことができます。例えば、 this.p_x = n
this を使うことは、通常必要ではありませんが、なにか、あいまいな状況で、役に立つことがあります。

以下に、テストのためのコードを紹介します。
これは、私たちが、 property を、それが通常のメンバーと同じように使うことができる方法を、示しています。
プログラムを走らせると、プログラムは、画面に表示します。これは、property の get/set コードが呼ばれていることを示しています。

現在、このコードは、かなり取るに足らないものです。しかし、この考えに慣れると、いくつかの良い用途に、これを付けることができるのが、分かるでしょう。
例えば、あなたが、GUIに書いている画像、TYPE が、画面の上のボタンを表しているとき、あなたは、button.text = "Hello World!" ができます。そして、property のコードを更新して、画面に変化を表示することができます。
または、たぶん、あなたは、ある種のリストを維持するために Type を使います。
あなたは、list.size += 10 として、いくらかのコードを property に置くことで、リストをより大きくすることができます。


構築子(Constructor)/解体子(Destructor)

構築子(Constructor) は、Type を作るとき(例えば、Dim を使うとき)、呼ばれる機能です。
解体子(Destructor) は、Type が範囲から出るときに呼ばれる機能です。
Type が範囲から出るとは、Type がメイン・コードにあるときは、プログラムが終わる時です。ローカルの Type の場合は、関数が終わるときです。
下の例を見てください。上に上げた例を、拡張しました。

Type bar
  Declare Constructor ()
  Declare Destructor ()
  Declare Property x() As Integer
  Declare Property x (ByVal n As Integer)
  p_x As Integer Ptr
End Type

Constructor bar ()
  Print "Constructor bar()"
  p_x = Allocate (SizeOf(Integer))
  *p_x = 10
End Constructor

Destructor bar ()
  Print "Destructor bar()"
  Deallocate(p_x)
End Destructor

Property bar.x() As Integer
  Print "bar.x()"
  Property = *p_x
End Property

Property bar.x (ByVal n As Integer)
  Print "bar.x(ByVal n As Integer)"
  *p_x = n
End Property

'---

Dim foo As bar

Print foo.x
foo.x = 5
Print foo.x
Sleep


再び、構文は、通常の関数と、よく似ています。
今回、私が、p_x を、Integer ptr になるように変えたことに、注意してください。
次に、foo が生成されたとき、構築子は、これのためのメモリを、Allocate します。そして、メモリにデフォルト値を与えます。
そして、このメモリを De-Allocateすると、メモリの内容は破壊されます。
つまり、あなたは、ConstructorDestructor を使って、あなたのためにセットアップして、次に、それがいったん終わったあとに掃除します。
Again a trivial example, but bring back the example of some kind of list, and having it set the list up for you, and clean it up when it's finished with can be quite handy.
再び、些細な例です。しかし、ある種のリストの例を、呼び戻します。あなたのために、これをリスト・アップします。終わるとき、かなり便利にクリーン・アップできます。


手続き

あなたは、Type の中に、通常の SubFunction を持つことができます。
SubFunction は、メソッドと呼ばれます。
下の例を見て下さい:

Type bar
  Declare Constructor ()
  Declare Destructor ()
  Declare Property x() As Integer
  Declare Property x (ByVal n As Integer)
  Declare Sub Mul5 ()
  Declare Function Addr() As Integer Ptr
  p_x As Integer Ptr
End Type

Constructor bar ()
  Print "Constructor bar()"
  p_x = Allocate (SizeOf(Integer))
  *p_x = 10
End Constructor

Destructor bar ()
  Print "Destructor bar()"
  Deallocate(p_x)
End Destructor

Property bar.x() As Integer
  Print "bar.x()"
  Property = *p_x
End Property

Property bar.x (ByVal n As Integer)
  Print "bar.x(ByVal n As Integer)"
  *p_x = n
End Property

Sub bar.mul5 ()
  *p_x *= 5
End Sub

Function bar.Addr() As Integer Ptr
  Function = p_x
End Function

'---

Dim foo As bar

Print foo.x
foo.x = 5
Print foo.x
foo.mul5 ()
Print foo.x
Print "address p_x points to", foo.Addr ()
Sleep


そこで、今、私たちは、5 だけ p_x で示される整数を掛ける Sub と、ポインタが保持するメモリ・アドレスを取得する関数を、加えました。


Private(非公開)/Public(公開)

デフォルトで、bar 型の全てのメンバーは、公開です。
これは、私たちが、メンバーを、読んだり、書いたり、または呼ぶことができることを意味します。
しかし、時には、あなたは、それらを非公開にしたいかもしれません。
私たちのメンバー p_x を例にとります。
私たちは、現在、Print *foo.p_x ができます。そして、それが示す値を、表示できます。
これを非公開にしたいと思います。bar 型(構築子、解体子、property、メソッド)のメンバーだけが、それにアクセスできるように。
このようにして、私たちは、私たちが選ぶ方法だけで p_x に対処するように、確保できます。
例えば、メインのコードで、'DeAllocate(foo.p_x)' をすると、解体子が稼働するとき、再びそれを解放しようとするでしょう。これは、'double free' として知られています。
Type 宣言を、下のように変えます:

Type bar
  Declare Constructor ()
  Declare Destructor ()
  Declare Property x() As Integer
  Declare Property x (ByVal n As Integer)
  Declare Sub Mul5 ()
  Declare Function Addr() As Integer Ptr
Private:
  p_x As Integer Ptr
End Type


今度は、Print *foo.p_x をメインのコードに追加してみてください。そして、コンパイルしてください。
fbc は、メッセージを表示します。 "error 173: Illegal member access, found 'p_x' in 'Print *foo.p_x'"
これは、コンパイラが、私たちが p_x を非公開にしたという事実を、今、実行していることを、示しています。
あなたが、private:public: を使うとき、 その命令文に続く、どのメンバーも、規則に従います。
ここに、構文を示すだけの、かなり無意味な例があります:

Type bar
Private:
  a As Integer
  b As Integer
Public:
  c As Integer
  d As Integer
Private:
  e As Integer
End Type


上の型で、メンバー abe は、非公開です。 cd は、公開です。


演算子の多重定義

演算子の多重定義は、私たちの Type にかかわるある種の操作を実行したい場合に、何をすべきかを、コンパイラに伝える方法です。
下の例を見てください:

Type bar
  n As Integer
End Type

Dim As bar x, y, z

z = x + y


今、通常、コンパイラは、これを見ると、コンパイラは、2つの Type を合計する方法がわからないので、エラーを投げるでしょう。しかし、私たちは、起こるようにしたいことを、定義できます。
ここに、方法があります:

Type bar
  n As Integer
End Type

Operator +(ByRef lhs As bar, ByRef rhs As bar) As bar
  Operator = Type (lhs.n + rhs.n)
End Operator

Dim As bar x, y, z

x.n = 5
y.n = 10
z = x + y
Print z.n
Sleep


このコードでは、私は、演算子の左側と右側の演算対象を示すのに、lhsrhs を使用します。
また、type(lhs.n + rhs.n) に注意してください。 これは返される Type を造ります。
下のような型の場合:

Type bar
  x As Integer
  y As Integer
  z As Integer
End Type


上は、type(xpart, ypart, zpart) のように建てることができます。

ほとんど、あるいは、すべての演算子は、多重定義できます。そして、その大部分は、二項演算子です。二項演算子は、上記の + の例のように、二つの演算対象があることを意味しています。
いつくかは、右側の演算対象しかない、単項演算子です。Not と、単項のマイナスが、そうです。
多重定義は、'Operator Not(ByRef rhs As bar) As bar'のようにします。

演算子が、Type の中で宣言されなければならない、いくつかの特別なケースがあります。
特別なケースとは、代入演算子と型変換です。

代入演算子は、+= -= mod= や、Let です。
Let は、代入をするとき、下のように使います:

Dim As bar foo
Dim As Integer x
foo = x


そして、型変換は、ちょうど、逆です。
別のデータ型に型変換するとき、型変換は、下のように使われます:

Dim As bar foo
Dim As Integer x
x = foo


下は、LetCast を使う、短い例です:

Type bar
  n As Integer
  Declare Operator Let (ByRef rhs As Integer)
  Declare Operator Let (ByRef rhs As String)
  Declare Operator Cast() As String
End Type

Operator bar.Let (ByRef rhs As Integer)
  n = rhs
End Operator

Operator bar.Let (ByRef rhs As String)
  n = Val (rhs)
End Operator

Operator bar.Cast() As String
  Operator = Str (n)
End Operator

Operator +(ByRef lhs As bar, ByRef rhs As bar) As bar
  Operator = Type (lhs.n + rhs.n)
End Operator

Dim As bar x, y, z

x = 5
y = "10"
z = x + y
Print z
Sleep


あなたは、サポートしたい各データ型のために、別々の let と cast を持つ必要があります。
型の中で宣言する必要がある演算子は、非静的として知られています。そして、型の中で宣言しなくてよいものは、グローバルとして知られています。
この技術的な理由があります。
非静的な演算子は、演算子が Type のどのインスタンス(専門用語で、上の例では、xbar のインスタンスです。)を参照しているかを知る必要があります。これは隠された 'this' 参照で、達成されます。
この隠された 'this' 参照は、演算子とメソッドのような他のメンバーが、呼び出しで、Type のどのインスタンスを参照しているかを知る方法です。
ほとんどの演算子を、多重定義することができます。 現在、多重定義できる演算のリストは、下の通りです。

特定演算子:
cast, @, [], new, new[], delete, delete[], for, step, next
代入演算子:
let, +=, -=, *=, &=, /=, \=, mod=, shl=, shr=, and=, or=, xor=, imp=, eqv=, ^=
単項演算子:
-, not, *, ->, abs, sgn, fix, frac, int, exp, log, sin, asin, cos, acos, tan, atn, len
二項演算子:
+, -, *, &, /, \, mod, shl, shr, and, or, xor, imp, eqv, ^, =, <>, <, >, <=, >=


構築子/メソッドの多重定義

普通の関数と同様に、Type の構築子とメソッドを、多重定義することができます。
構築子のために、多重定義は、インスタンスの構成の仕方の詳細を指定する方法を、提供します。
ここに、短い例があります:

Type bar
  Declare Constructor ()
  Declare Constructor (ByVal initial_val As Integer)
  x As Integer
End Type

Constructor bar ()
  x = 10
End Constructor

Constructor bar (ByVal initial_val As Integer)
  x = initial_val
End Constructor

Dim foo As bar
Print foo.x

Dim baz As bar = bar (25)
Print baz.x
Sleep


一行目の Constructor は、引数を持っていなくて、デフォルトの構築子として知られています。
これは、foo.x に、初期値 10 をセットアップします。
しかし、私たちは、また、初期値を受け入れる、別の構築子を指定しました。
私たちが、これを頼む方法が、Dim baz As bar = bar(25) を呼ぶことなのに注意してください。
また、あなたはデフォルト構築子を省くことができます。そして、引数を取る構築子を使って、いつも初期値を指定しなければなりません。
あなたは、多重定義の解体子を持つことができません。手動で、どれを呼ぶかを選択する方法が、ないからです。

メソッドの多重定義は、Constructor の多重定義と、よく似ています:

Type bar
  Declare Sub foo ()
  Declare Sub foo (ByVal some_value As Integer)
  Declare Sub foo (ByRef some_value As String, ByVal some_other As Integer)
  x As Integer
End Type


これらは、通常の多重定義の関数と同じように、働きます。


終わりに

このチュートリアルが、あなたの役に立つことを願っています。ただ、まだ学ぶことがいくつか残っています。
あなたがもっと知りたいなら、あなたがそれらを拾い上げることは、それほど難しくありません。
wiki とフォーラムで、利用できる、多くの情報が有ります。そして、このチュートリアルの第二部 - オブジェクトとしての型 入門(2) も有ります。

以下も参照下さい

いっしょに学ぼう に戻る
←リンク元に戻る プログラム開発関連に戻る

最後、Sancho3 によるレビュー(2018年2月06日)

ページ歴史:2018-04-24 03:24:19
日本語翻訳:WATANABE Makoto、原文著作者:YetiFoot

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

表示-非営利-継承