MQL4言語の基礎1
1、構造体とクラスの違い
(1).宣言として使用されるclassは主要語(キーワード)となります。
英文ではThe keyword class is used in declaration.
となりますが、keyword の意味がはっきりしません。
キーカレンシーが基軸通貨なので、キーワードは主要語、
つまり中心となる語であること強調しているものと解釈しました。
(2).クラスのメンバーはデフォルトではprivateとなり、
構造体のメンバーはデフォルトではpublicとなります。
クラスおよび構造体のメンバーは、
キーワードprivate、public、protectedを指定し、
カプセル化できます。
指定されていないとき、つまりデフォルトのときですが、
このときは上記のようにクラスと構造体では違いがあります。
(private、public、protectedの簡単な説明)
privateを指定したメンバーは、クラスのメンバー関数からと、
クラスでフレンド指定された関数からのみ使用できます。
publicを指定したメンバーはどの関数からでも使用できます。
protectedを指定したメンバーは、クラスのメンバー関数からと、
クラスでフレンド指定された関数からと、
このクラスから派生クラスの中の関数からのみ使用できます。
(3).クラスオブジェクトはvirtual を定義出来ますが、
構造体はvirtual functionを定義出来ません。
(virtual functionの簡単な説明)
ベースクラスと派生クラスの間で用いられますが。
ベースクラスでvirtual指定された関数は、
派生クラスの同じ名前の関数で置き換えられます。
(4).クラスオブジェクトはnew operatorを使うことが出来ますが、
構造体はnew operatorを使うことが出来ません。
(new operatorの簡単な説明)
オペレータnewはオブジェクトを動的に生成します。
オペレータdeleteはそのオブジェクト開放します。
オブジェクトの動的生成を配列で例に説明すると、
double arr[10]; は1次元の配列で10個の値を入力できますが、
double arr[]; は1次元の配列でも、何個の値を入力するか定まっていません。
前もって個数を分かっていなくても、
限定しないで使うことが出来るようになります。
これと同じようにクラスオブジェクトも動的に生成すると同じことが出来ます。
(5).クラスはクラスでのみ継承出来、構造体は構造体でのみ継承出来ます。
(継承の簡単な説明)
継承とは、基礎となるクラスを元に、
新しいメンバー加えた派生クラスを定義することです。
下のプログラムは、クラスと構造体のデフォルト、
クラスの継承と構造体の継承、
クラスと構造体のvirtual functionの違い、
そしてクラスと構造体のnew operatoの違いをテストしたものです。
プログラムを色々変えて試してみてください。
//+------------------------------------------------------------------+
2、コンストラクタ 1
わかりやすい説明にするために比喩がよく使われますが、
クラスの説明に比喩を多用し、かえって混乱の原因になっているのは、
C++の解説書を見てもわかる通りです。
例えば、ベースクラスが生物で派生クラスは植物であると説明するのは、
あまり良い説明とは言えません。
クラスでは抽象的すぎて分りにくいので、
ロバート・ルイス・スティーヴンソン著の宝島の場合を考えて見ましょう。
宝島は、主人公達の宝探し、海賊との闘いを描いた子供向けの海洋冒険小説です。
試しに、小説の中で、宝島の地図の持ち主が、
「宝島はマン島のような島である。」と、たとえで話してみる場面を想像してみます。
しかしこれでは小説の主人公達も、読者にも、
海賊の隠した財宝のある大西洋の孤島の位置はさっぱり分りません。
宝島に行くには、
地図と、実際に宝島に行った経験のある人間と、船が必要です。
クラスの場合、
データや関数を、カプセルで分ける事にあるのですから、
クラスを集合で考えるのは良いでしょう。
しかし、クラスも宝島の話と同じで、
たとえ話を強調すると、かえって混乱の原因になってしまいます。
現代的に表現するなら、宝島の位置は経度、緯度で表すべき所です。
同じように、集合も別な分野ではなく、大きな視点で見る必要があるでしょう。
この場合、集合はベン図式で考えるのが適当ではないかと思います。
コンストラクタの定義
1.コンストラクタとクラスメンバーの初期化
コンストラクタは、構造体やクラスのオブジェクトを生成するとき、
自動的に呼び出され、クラスメンバーを初期化するために使われる関数です。
2.コンストラクタと構造体の初期化
構造体もクラスと同様に定義されます。
3.コンストラクタはvoidタイプ
コンストラクタの名前はクラスの名前と同じで、値を返さないvoidタイプの関数です。
4.コンストラクタと、string、dynamic arrys、オブジェクト
特別に初期化が必要な、string、dynamic arrys、オブジェクトのメンバーの定義と、
コンストラクタがあるかどうかとは関係がありません。
5.複数のコンストラクタの定義
クラスは複数のコンストラクタを持つことが出来ます。
各コンストラクタは複数のパラメータ(引数)の初期化リストによりそれぞれ初期化されます。
(初期化リスト:詳しくはコンストラクタ 2で説明の予定)
6.デフォルトコンストラクタ
パラメータ(引数)を持たないコンストラクタです。
クラス宣言でコンストラクタがないとき、コンパイラは自動的にデフォルトコンストラクタを生成します。
パラメータ(引数)を持たないオブジェクトの生成に必要となります。
クラスオブジェクトのarryを初期化する時に必要となります。
(ユーザがコンストラクタを1つでも定義すると、デフォルトコンストラクタは自動的に生成されないので、
クラスオブジェクトのarryを使う時には、ユーザがデフォルトコンストラクタを定義する必要があります。)
7.パラメトリックコンストラクタ
一つあるいは複数のパラメータ(引数)を持つコンストラクタです。
8.コピーコンストラクタ
複数のコンストラクタの一つです。
クラスオブジェクトのコピーの時に必要となります。
9.コピーコンストラクタの定義
コンストラクタ名(const クラス名 &参照オブジェクト名){関数;}
参照タイプ(&)の、定数(const)、オブジェク(参照オブジェクト名)を引数に持つ、
コンストラクタ関数(コンストラクタ名)です。
以下はデフォルトコンストラクタ、パラメトリックコンストラクタ、コピーコンストラクタを
使用したプログラミングの例です。
//+------------------------------------------------------------------+
#property copyright "T.S"
#property link "http://"
#property version "1.00"
#property description "Constructors 1"
class MyDateClass1
{
//private:
public:
int m_year1; // Year
string m_name;
public:
//---automatically Default constructor
int get1();
string gets();
};
int MyDateClass1::get1()
{
return(m_year1);
}
string MyDateClass1::gets()
{
return(m_name);
}
//-------------
class MyDateClass2
{
//
private:
int m_year2; // Year
int m_month2;
public:
//--- Default constructor
MyDateClass2(void);
//--- A Parametric constructor
MyDateClass2(int m);
//--- A copying constructor
MyDateClass2(const MyDateClass2 &x){m_year2=x.m_year2;m_month2=x.m_month2;}
//------
int get2_y();
int get2_m();
};
MyDateClass2::MyDateClass2(void)
{
//---
MqlDateTime mdt;
datetime t=TimeCurrent(mdt);
m_year2=mdt.year;
// Print(__FUNCTION__);
// Print(x);
}
MyDateClass2::MyDateClass2(int m)
{
m_month2=11;
}
int MyDateClass2::get2_y()
{
return(m_year2);
}
int MyDateClass2::get2_m()
{
return(m_month2);
}
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
//Automatically Default constructor
MyDateClass1 mydate0;
mydate0.m_year1=2013;
mydate0.m_name="Sakata" ;
Print(mydate0.get1()," ",mydate0.gets());
//Automatically Default constructor and arry
MyDateClass1 mydate1[2];
mydate1[0].m_year1=2014;
mydate1[1].m_year1=2015;
Print(mydate1[0].get1()," ",mydate1[1].get1());
//Default constructor
MyDateClass2 mydate2;
MyDateClass2 mydate3();
//A parametric Default constructor
MyDateClass2 mydate4(11);
Print(mydate2.get2_y()," ",mydate3.get2_y()," ",mydate4.get2_m());
//A copying constructor
MyDateClass2 mydate5=mydate3;
MyDateClass2 mydate6(mydate4);
Print(mydate5.get2_y()," ",mydate6.get2_m());
}
//+------------------------------------------------------------------+
3、コンストラクタ2、デストラクタ、関数メソッド、新しいクラス
コンストラクタ2(初期化リスト)
1.初期化リストはコンストラクタでのみ使用できます。
2.初期化リストは関数の定義(実施)のとき行われます。
3.各メンバは、クラスメンバ(リスト)、の形で初期化されます。
4.初期化リストのメンバは任意の順でかまいませんが、
すべてメンバの表示順序(定義順序)に従って初期化されます。
5.表示の最初のメンバが初期化され、そして次が初期化されます。
6.順番は他のメンバとの評価による事を計算に入れておきましょう。
7.親メンバの初期化リストは初期化されないので、
8.べースクラスでデフォルトコンストラクタが定義されていないとき、
かつ一つあるいはそれ以上のコンストラクタが定義されているとき、
初期化リストでベーススクラスのコンストラクタの一つを常に呼び出さなければなりません。
9.初期化リストがどのようであっても、コンマで区切られたリストの通常のメンバの中で、
オブジェクトの初期化が始めに呼び出されます。
デストラクタ
1.デストラクタはクラスオブジェクトを消去するとき自動的に呼び出される特殊な関数です。
2.デストラクタは、名前の前にチルド(~)つけたクラス名で書きます。
3.デイニシャライゼーションが要求されるString、動的配列、オブジェクトは、
デストラクタがあるかないかに関わりなく、常にデイニシャライゼーションされます。
4.デストラクタがあればこれらの行為はデストラクタが呼び出された後に行われます。
5.キーワードvirtualが宣言されているかないかに関わらず、デストラクタは常にvirtualです。
(注:C++ではデストラクタは常にvirtualでない。)
関数メソッド
1.クラスの関数メソッドはクラスの内と外で定義されます。
2.クラス内でのメソッドの定義は、メソッドの定義の後ろ(右に)コードがきます。
3.クラスの外の関数のコードは、スコープレゾリュ-ションオペレータ(::)が使われます。
4.クラス名がスコープの前にきます。
新しいクラス
1.新しいクラスを開発は、メンバへの外からのアクセスの制限が求められます。
2.この目的の為に、private、protectedが使用されます。
3.この場合、同じクラスからのみアクセス出来るようにすることで、データを隠蔽します。
4.キーワードprotectedが使用されるときは、隠蔽データは継承クラスのメソッドからのアクセスも出来ます。
5.もしメンバのアクセスを完全にオープンにするときは、キーワードpublicを使用します。
6.public:の後に宣言されたクラスのメンバとメソッドは、プログラムによりクラスオブジェクトへ参照が可能となります。
7.private:の後に宣言された任意のメンバは、このクラスの関数でのみ可能となります。
8.常に後ろにコロン(:)が書かれ、何回でもクラス内で定義できます。
9.ベースクラスのメンバへのアクセスは派生クラスの継承で再定義されます。
下記プログラム
1.クラスCPerson1は、初期化リストのプログラミングの例です。
2.親クラスCParentと派生クラスCDerivedの初期化のときは、
始に派生クラスのderivedオブジェクトが生成され、
デフォルトコンストラクタCDerived()が呼び出されると、
ペアレントのCParentのコンストラクタが呼ばれます。
それからクラスメンバmemberのコンストラクタを行います。
/+------------------------------------------------------------------+
#property copyright "T.S"
#property link "http://"
#property version "1.00"
#property script_show_inputs
#property description "Constructors 2,destructor,Class function-methods and New class"
class CPerson1
{
string name1;
string name2;
string name3;
public:
//--- A constructor with an initialization list
CPerson1(string iname1,string iname2,string iname3):name2(iname2),name3(iname1),name1(iname3){ };
void PrintName(){Print(name1," ",name2," ",name3);};
};
//----------
class CParent
{
string name1;
public:
//--- A constructor with an initialization list
CParent(string name) : name1(name) { Print(name1);}
};
class CDerived : CParent
{
// A class member is an object of the parent
CParent member;
public:
//--- A default constructor in the initialization list calls the constructor of a parent
CDerived(): CParent("CDerived"), member(_Symbol) { };
};
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
CPerson1 peaple1("Asada","Baba","Cojima");
CPerson1 *peaple2 = new CPerson1("Kurokawa","Kida","Kaga");
//--- Output values
peaple1.PrintName();
peaple2.PrintName();
//--- Delete a dynamically created object
delete peaple2;
//---
CDerived derived;
}
//+------------------------------------------------------------------+
void、NULL定数、オブジェクトポインタ、値渡しと参照渡し、this
------------------------------------------------------------
voidタイプ
voidタイプは、構文的にほかのタイプと同じ基本タイプです。
関数が値を返さないとき、あるいはパラメータ(引数)のない事を意味する関数パラメータとして、
voidは使用されます。
ほかの基本タイプとは
(char uchar bool short ushort int uint color long ulong datetime float double string)
NULL定数
1.NULLは前もって定義された定数で、voidタイプです。
2.NULLのときは、変数に基本タイプの指定をしなくても、割り当てることが出来ます。
NULL値を持つ基本タイプの変数の時は認められます。
参考1
( if(some_string==NULL) some_string="empty"; )
3.NULLは、new operatorで生成されたオブジェクトのポインタとしても、割り当てることが出来ます。
参考2
(NULLポインタはnew operatorで生成されたオブジェクトのポインタと同じく
deleteで削除出来ます。NULLポインタは値として0を持ちます。)
オブジェクトポインタ
MQL5では、複雑な、ダイナミックオブジェクトを生成することが可能です。
これにはnew operatorが使われ、生成オブジェクトのデイスクリプタが戻されます。
デイスクリプタは8バイトのlargeです。
構文的にはMQL5のオブジェクトデイスクリプタはC++のポインタと同じです。
値渡しと参照渡し(関数)
MQL5では、シンプルタイプのパラメータは値渡しと参照渡しの両方が可能です。
混合タイプのパラメータも常に参照渡し出来ます。
参照渡しのパラメータは、アンパーサンド(&)をパラメータの名前の前に付けます。
this
リザーブワードthisはクラスのオブジェクトを参照するために使われます
クラス、構造体内部のメソッドから利用されます。
thisは常に、使用されるメソッドで参照されます。
式GetPointer(this)は、
関数メンバから呼ばれたGetPointer()の働きで、オブジェクトのポインタを与えます。
MQL5ではオブジェクトを返しませんが、オブジェクトのポインタを返します。
このように、オブジェクトを返すための関数が必要な時は、、
GetPointer(this)のように、このオブジェクトのポインタを返すことが出来ます。
参考3
(動的オブジェクトの関数メンバのメモリ)
オブジェクトはそのクラスで定義(動的定義のとき)されたデータメンバのコピーを含んでいますが、
メンバ関数のコードのコピーは、含まれていなません。
関数コードのコピーは、クラスオブジェクト全体で一つだけ別に、メモリに保存されます。
参考4
1.C++では変数に&を付けると、変数のアドレスを表します。
2.C++ではアドレスを格納するための変数がポインタになります。
int *s はint型の変数のアドレスを格納するポインタsを宣言しています。
しかし、間違えたメモリの使用は、PCのシステムを壊してしまうことになるので、
MQL5では、どちらも使用できません。
下記のプログラミングコードの例では、メソッドgetThis(void)は、
CThis *CThis::getThis(void)
{
return(GetPointer(this));
}
です。
少し複雑ですが、
メソッドの戻し値のタイプがクラスのポインタCThis *で、
スコープ名CThisの、
メソッド名getThis(void)の関数の定義が、
{ return(GetPointer(this)); }
となります。
//+------------------------------------------------------------------+
#property copyright "T.S"
#property link "http://"
#property version "1.00"
#property script_show_inputs
#property description "this,pointer"
class CThis
{
private:
double m;
public:
double get(void);
CThis *getThis();
};
double CThis::get(void)
{
return(m);
}
CThis *CThis::getThis(void)
{
return(GetPointer(this));
}
void OnStart()
{
//---
CThis *mythis;
mythis =new CThis;
string p1="";
string p2="";
if(CheckPointer(mythis.getThis())== POINTER_DYNAMIC)p1="Dynamic1";
else p1="No";
if(CheckPointer(mythis)== POINTER_DYNAMIC)p2="Dynamic2";
else p2="No";
Print(p1," ",p2);
delete mythis;
if(CheckPointer(mythis)== POINTER_DYNAMIC)p2="Dynamic2";
else p2="No";
Print(p2);
//---try !
/*
int a;
a=5;
//* is pointer cannot be used
int *b;
//& is operand expected
b=&a;
*/
}
//+------------------------------------------------------------------+
4、オブジェクト-オリエントプログラミング(OOP)の概要
オブジェクト-オリエントプログラミング(OOP)は、
主としてデータに焦点をあててプログラミングし、
データと動作は密接な関係にあります。
データと動作でクラスが構成され、オブジェクトはクラスのインスタンスになります。
オブジェクト-オリエントアプローチの構成要素は以下のようになります。
カプセル化と型拡張性
継承
多態性
オーバーロード(多重定義)
仮想関数
OOPは、動作モデルとして、コンピュータの操作を考えます。
モデル化されたアイテムは、抽象的なコンピュータの操作により表されたオブジェクトです。
よく知られているゲーム、テトリスを書きたい場合を考えてください。
そのためには、4個の正方形の角を互いに会わせ構成された、
ランダムな形の外観を、どのようにモデル化するのかを学ばなければなりません。
落下スピード、ローテーション操作の定義、そして形のシフト調整をする必要があります。
スクリーンでの形の動きは、縦穴の境界に制限されます。この制限はモデル化されなければなりません。
加えて、転がって落ちたキューブは破壊されて、獲得した得点がカウントされなければなりません。
このように理解のやさしいゲームは、
形モデル、縦穴(well)モデル、形移動モデル、その他いくつかのモデルを作る必要があります。
全てこれらのモデルは、コンピュータ計算で表現される抽象概念です。
これらのモデルを表現するために、抽象データタイプADT(あるいは複雑なデータタイプ)が使用されます。
厳密に言うと、DOMでの"shapes"動作モデルはデータタイプではありませんが、
"shapes"データタイプの操作の集合で、
制限された"well"データタイプを使用しています。
オブジェクトはクラス変数です。
オブジェクトオリエントプログラミングでは、簡単にADTを生成、使用できるようになります。
オブジェクトオリエントプログラミングは、継承メカニズムを使用しています。
継承は、すでにユーザによって定義されたデータタイプから得られた派生タイプです。
例えばテトリスの形を作るには、先にべースクラスShapeを作るのが便利です。
7つの形を表現するクラスは、そのベースクラスから派生し、
各々の形における動作の構成は、派生クラスで定義されます。
OOPでは、オブジェクトがそれらの動作に関する応答をします。
ADT開発者は、対応するオブジェクトで通常期待される、
動作を表すコードを含ませなければなりません。
オブジェクト自身がその動作の応答をするという事実は、
ユーザに関するオブジェクトプログラミングのタスクを大きく簡略化します。
もしスクリーンに形を描きたいなら、中心がどこにあるか、
それをどのように描くか知る必要があります。
もしseparate shapeが、drawするためどのようするか定義されているなら、
プログラマーは形を使用するとき、"draw"メッセージを送らなければなりません。
MQL5言語はC++に似ています。
そして、ADTを実装する隠蔽メカニズムを持ちます。
隠蔽は、内部の詳細の実装(*注釈:プライベート-メンバのこと)を扱える、
特殊なタイプを組み込む一方、
外部からアクセスでき、オブジェクトのこのタイプを扱える関数を組み込みます。
このタイプを使用するプログラムから、詳細の実装は受けつけません。
OOPのコンセプトは、以下に記載の、関連するコンセプトの集合です。
実世界における行為のシミュレーション
ユーザ定義のデータタイプ
実施する詳細の隠蔽
継承によりコードの再利用が可能
実行中のファンクションコールの解読
これらコンセプトの幾つかはむしろあいまいで、幾つかは抽象的、他は一般的です。
ADTの例として、MQL5スタンダードライブラリーのベースクラスであるCObjectがあります。
CObjectクラスはObject.mqhのファイルにあり、
MeTaEditorの左にあるNavigatorのIncludeデイレクトリーに保管されています。
CObjectは、MeTaEditorに格納されているMQL5スタンダードライブラリーの、
ほとんどのベースクラスとして使われおり、特に重要なクラスです。
学習の参考のため、CObjectのプログラムコードを以下に載せてあります。
(変更、手直し等の手は一切加えておりません。)
C++に比べると、機能はシンプルです。
オブジェクトの記憶にlistを使うのは、
メモリーの使用を少なくするためと、
listが配列より扱いやすいためです。
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Object.mqh |
//| Copyright 2009-2013, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#include "StdLibErr.mqh"
//+------------------------------------------------------------------+
//| Class CObject. |
//| Purpose: Base class for storing elements. |
//+------------------------------------------------------------------+
class CObject
{
private:
CObject *m_prev; // previous item of list
CObject *m_next; // next item of list
public:
CObject(void): m_prev(NULL),m_next(NULL) { }
~CObject(void) { }
//--- methods to access protected data
CObject *Prev(void) const { return(m_prev); }
void Prev(CObject *node) { m_prev=node; }
CObject *Next(void) const { return(m_next); }
void Next(CObject *node) { m_next=node; }
//--- methods for working with files
virtual bool Save(const int file_handle) { return(true); }
virtual bool Load(const int file_handle) { return(true); }
//--- method of identifying the object
virtual int Type(void) const { return(0); }
//--- method of comparing the objects
virtual int Compare(const CObject *node,const int mode=0) const { return(0); }
};
//+------------------------------------------------------------------+
5、オブジェクト-オリエントプログラミング(OOP)の概要
型のカプセル化と型拡張性
------------------------------------------------------------
OOPはソフトウエアを書くためのバランスの取れたアプローチです。
データとビヘイビア(振る舞い)は共にパックされます。
カプセル化により、ユーザ定義のデータタイプを生成し、
言語のデータタイプを拡張し、そしてお互いに影響し合います。
幸いにも、型拡張性により言語のユーザ定義データタイプがさらに加えられ、
基本タイプ(訳注:char、int、double等)と同様、簡単に使えるようになります。
抽象データタイプ、例えば、究極の表現では、
よく知られたビヘイビアタイプの、あるデータ列(string)。
データ列(string)のユーザは、stringの操作が、
連結(データ列の)やprint(データ列の出力)のような、
確実なビヘイビアを持つことを知っています。
連結やprintの操作はメソッドと呼ばれています。
ADT(抽象データタイプ)の確実な実装は、幾つかの制限があります。
例えば、長さに制限のあるデータ列(string)。
これらの制限は、パブリックなビヘイビアに影響を与えます。
しかし、内部あるいはプライベートなインプリメンテーションの詳細は、
ユーザが見ているオブジェクトの進行に、直接影響を与えません。
(訳注:C++の設計では、インプリメンテーションはプライベートメンバー、
パブリックメンバーはインターフェイスのこと。)
例えば、データ列(string)はしばしば配列として実装されますが、
この配列の内部でのベーシックなアドレスと、
その名前はユーザにとって必要ではありません。
(訳注:配列のほかにはリストがあります。
アドレスや名前は、クラスのプライベートなメンバとなります。)
ユーザ定義タイプのオープン-インターフェイスを提供する場合、
カプセル化はインプリメンテーションの詳細を隠すことが出来ます。
C++と同様 MQL5では、
キーワードprivate、protected、publicと組み合わせて、
クラスと構造体の定義(classとstruct)によるカプセル化に使われます。
publicキーワードにより、隠れているメンバーにアクセス出来、
無制限にオープンとなります。
このキーワードが無いと、クラスのメンバはデフォルトではロックされます。
プライベートメンバは、そのクラスからとメンバ関数からのみアクセス出来ます。
プロテクトのクラス関数は、そのクラスと、継承クラスからアクセスが可能です。
パブリックなクラス関数は、クラスのスコープ内での宣言で使用可能となります。
プロテクションは、
データ構造を変えることが予め期待されていない、
クラスの実装の一部を、隠す事を可能にします。
アクセス制限、データ隠蔽はオブジェクト-オリエントプログラミングの特色となっています。
6、継承 1
OOPの典型的な特徴は、継承によるコードの再利用の推進です。
新しいクラスは、いま在るベースックラスと呼ばれるものから作られます。
派生クラスはベースクラスのメンバーを使用し、
それを修正し、補う事が出来ます。
沢山の型は、存在する型からのヴァリエーションとなります。
それら新しいコードの開発は、しばしば退屈です。
さらに、新しいコードはエラーを暗示します。
派生クラスはベースクラスの定義説明を継承し、
さらにコードの再開発とテストが必要です。
継承関係は階層的です。
階層は、全て多様、複雑で、要素をコピーすることが認められています。
それはオブジェクトを分類します。
例えば、元素の周期表の中には気体もありますが、
(訳注:ヘリウムガス、ネオンガス、アルゴンガス等)
それは、周期元素の全てに本来備わっている属性です。
ガスは次のシステムの一部になります。
不活性ガスは、次の重要なサブクラス(下位分類)を与えます。
不活性ガスの次の階層は、アルゴンのようなガスです。
階層は、明らかに不活性ガスの振る舞いとして解釈することが出来ます。
私達はそれらの原子が陽子と電子から成り立ていることを知っています。
ほかの全ての元素に関しても同じです。他のガスも同様です。
他の元素との通常の化学的な結合をするガスは、
不活性ガスのサブクラスではありません。
幾何学的形状の継承の例を考えてみましょう。
簡単な形(円、三角形、長方形、正方形、その他)の多様なバラエテイを描くには、
すべての派生クラスの原型、ベストな方法である、
ベースクラス(ADT)を生成することが大事です。
ベースクラスCShapeを作ってみましょう。
形を描くための最も共通するメンバを持たせます。
これらのメンバは、ある形の特徴的なプロパテイ、形のタイプ、主要な固定点の座標となります。
//--- The base class Shape
class CShape
{
protected:
int m_type; // Shape type
int m_xpos; // X - coordinate of the base point
int m_ypos; // Y - coordinate of the base point
public:
CShape(){m_type=0; m_xpos=0; m_ypos=0;} // constructor
void SetXPos(int x){m_xpos=x;} // set X
void SetYPos(int y){m_ypos=y;} // set Y
次に、ベースクラスから新しい派生クラスの生成しますが、
それは必要な面の広がりを加えた、特定のクラスを作ります。
円形に関しては、半径の値を持つメンバを加える必要があります。
正方形は辺の値により特徴付けられます。
ベースクラスCShapeから継承した派生クラスは次のようになります。
//--- The derived class circle
class CCircle : public CShape // After a colon we define the base class
{ // from which inheritance is made
private:
int m_radius; // circle radius
public:
CCircle(){m_type=1;} // constructor, type 1
};
正方形クラスの宣言も同様です。
//--- the derived class Square
class CSquare : public CShape // After a colon we define the base class
{ // from which inheritance is made
private:
int m_square_side; // square side
public:
CSquare(){m_type=2;} // constructor, type 2
};
始めにベースクラスのコンストラクタが呼ばれ、
次に派生クラスのコンストラクタ呼ばれ、
オブジェクトが生成されることに注意してください。
オブジェクトが消去されるときは、
始めに派生クラスのデストラクタが呼ばれ、
次にベースクラスのデストラクタが呼ばれます。
このように、最も一般的なベースクラスのメンバを宣言することで、
特殊なクラスである派生クラスにメンバを更に加えることが出来ます。
継承は何回も再使用できるパワフルなライブラリコードを生成することが出来ます。
+------------------------------------------------------------------------+
(1).宣言として使用されるclassは主要語(キーワード)となります。
英文ではThe keyword class is used in declaration.
となりますが、keyword の意味がはっきりしません。
キーカレンシーが基軸通貨なので、キーワードは主要語、
つまり中心となる語であること強調しているものと解釈しました。
(2).クラスのメンバーはデフォルトではprivateとなり、
構造体のメンバーはデフォルトではpublicとなります。
クラスおよび構造体のメンバーは、
キーワードprivate、public、protectedを指定し、
カプセル化できます。
指定されていないとき、つまりデフォルトのときですが、
このときは上記のようにクラスと構造体では違いがあります。
(private、public、protectedの簡単な説明)
privateを指定したメンバーは、クラスのメンバー関数からと、
クラスでフレンド指定された関数からのみ使用できます。
publicを指定したメンバーはどの関数からでも使用できます。
protectedを指定したメンバーは、クラスのメンバー関数からと、
クラスでフレンド指定された関数からと、
このクラスから派生クラスの中の関数からのみ使用できます。
(3).クラスオブジェクトはvirtual を定義出来ますが、
構造体はvirtual functionを定義出来ません。
(virtual functionの簡単な説明)
ベースクラスと派生クラスの間で用いられますが。
ベースクラスでvirtual指定された関数は、
派生クラスの同じ名前の関数で置き換えられます。
(4).クラスオブジェクトはnew operatorを使うことが出来ますが、
構造体はnew operatorを使うことが出来ません。
(new operatorの簡単な説明)
オペレータnewはオブジェクトを動的に生成します。
オペレータdeleteはそのオブジェクト開放します。
オブジェクトの動的生成を配列で例に説明すると、
double arr[10]; は1次元の配列で10個の値を入力できますが、
double arr[]; は1次元の配列でも、何個の値を入力するか定まっていません。
前もって個数を分かっていなくても、
限定しないで使うことが出来るようになります。
これと同じようにクラスオブジェクトも動的に生成すると同じことが出来ます。
(5).クラスはクラスでのみ継承出来、構造体は構造体でのみ継承出来ます。
(継承の簡単な説明)
継承とは、基礎となるクラスを元に、
新しいメンバー加えた派生クラスを定義することです。
下のプログラムは、クラスと構造体のデフォルト、
クラスの継承と構造体の継承、
クラスと構造体のvirtual functionの違い、
そしてクラスと構造体のnew operatoの違いをテストしたものです。
プログラムを色々変えて試してみてください。
//+------------------------------------------------------------------+
#property copyright "T.S" #property link "http://" #property version "1.00" #property description "Classes differ from structures " class MyDateClass1 { public: int m_year; }; class MyDateClass2 : public MyDateClass1 { public: int m_month; MyDateClass2(int mm); }; MyDateClass2::MyDateClass2(int mm) { m_month=mm; } struct MyDatestruct1 { int m_day; }; struct MyDatestruct2 : MyDatestruct1 { int m_hour; MyDatestruct2() {m_hour=21;}; }; class MyDateClass3 { public: int m_minute; MyDateClass3(){m_minute=30;}; ~MyDateClass3(){}; virtual void fun3(); }; struct MyDatestruct3 { int m_second; MyDatestruct3() {m_second=45;}; //compile error //virtual void fun4(); }; void OnStart() { //--- MyDateClass1 mydate1={2014}; int y=mydate1.m_year; MyDateClass2 mydate2(11); int m=mydate2.m_month; MyDatestruct1 mydate3={8}; int d=mydate3.m_day; MyDatestruct2 mydate4; int h=mydate4.m_hour; //--- MyDateClass3 *mydate5; mydate5 =new MyDateClass3; int m1=mydate5.m_minute; /* //compile error MyDatestruct3 *mydate6; mydate6 =new MyDatestruct3; */ Print(y," ",m," ",d," ",h," ",m1); //If skip --> Compile is ok,but memory leak error. delete mydate5; }//+------------------------------------------------------------------+
2、コンストラクタ 1
わかりやすい説明にするために比喩がよく使われますが、
クラスの説明に比喩を多用し、かえって混乱の原因になっているのは、
C++の解説書を見てもわかる通りです。
例えば、ベースクラスが生物で派生クラスは植物であると説明するのは、
あまり良い説明とは言えません。
クラスでは抽象的すぎて分りにくいので、
ロバート・ルイス・スティーヴンソン著の宝島の場合を考えて見ましょう。
宝島は、主人公達の宝探し、海賊との闘いを描いた子供向けの海洋冒険小説です。
試しに、小説の中で、宝島の地図の持ち主が、
「宝島はマン島のような島である。」と、たとえで話してみる場面を想像してみます。
しかしこれでは小説の主人公達も、読者にも、
海賊の隠した財宝のある大西洋の孤島の位置はさっぱり分りません。
宝島に行くには、
地図と、実際に宝島に行った経験のある人間と、船が必要です。
クラスの場合、
データや関数を、カプセルで分ける事にあるのですから、
クラスを集合で考えるのは良いでしょう。
しかし、クラスも宝島の話と同じで、
たとえ話を強調すると、かえって混乱の原因になってしまいます。
現代的に表現するなら、宝島の位置は経度、緯度で表すべき所です。
同じように、集合も別な分野ではなく、大きな視点で見る必要があるでしょう。
この場合、集合はベン図式で考えるのが適当ではないかと思います。
コンストラクタの定義
1.コンストラクタとクラスメンバーの初期化
コンストラクタは、構造体やクラスのオブジェクトを生成するとき、
自動的に呼び出され、クラスメンバーを初期化するために使われる関数です。
2.コンストラクタと構造体の初期化
構造体もクラスと同様に定義されます。
3.コンストラクタはvoidタイプ
コンストラクタの名前はクラスの名前と同じで、値を返さないvoidタイプの関数です。
4.コンストラクタと、string、dynamic arrys、オブジェクト
特別に初期化が必要な、string、dynamic arrys、オブジェクトのメンバーの定義と、
コンストラクタがあるかどうかとは関係がありません。
5.複数のコンストラクタの定義
クラスは複数のコンストラクタを持つことが出来ます。
各コンストラクタは複数のパラメータ(引数)の初期化リストによりそれぞれ初期化されます。
(初期化リスト:詳しくはコンストラクタ 2で説明の予定)
6.デフォルトコンストラクタ
パラメータ(引数)を持たないコンストラクタです。
クラス宣言でコンストラクタがないとき、コンパイラは自動的にデフォルトコンストラクタを生成します。
パラメータ(引数)を持たないオブジェクトの生成に必要となります。
クラスオブジェクトのarryを初期化する時に必要となります。
(ユーザがコンストラクタを1つでも定義すると、デフォルトコンストラクタは自動的に生成されないので、
クラスオブジェクトのarryを使う時には、ユーザがデフォルトコンストラクタを定義する必要があります。)
7.パラメトリックコンストラクタ
一つあるいは複数のパラメータ(引数)を持つコンストラクタです。
8.コピーコンストラクタ
複数のコンストラクタの一つです。
クラスオブジェクトのコピーの時に必要となります。
9.コピーコンストラクタの定義
コンストラクタ名(const クラス名 &参照オブジェクト名){関数;}
参照タイプ(&)の、定数(const)、オブジェク(参照オブジェクト名)を引数に持つ、
コンストラクタ関数(コンストラクタ名)です。
以下はデフォルトコンストラクタ、パラメトリックコンストラクタ、コピーコンストラクタを
使用したプログラミングの例です。
//+------------------------------------------------------------------+
#property copyright "T.S"
#property link "http://"
#property version "1.00"
#property description "Constructors 1"
class MyDateClass1
{
//private:
public:
int m_year1; // Year
string m_name;
public:
//---automatically Default constructor
int get1();
string gets();
};
int MyDateClass1::get1()
{
return(m_year1);
}
string MyDateClass1::gets()
{
return(m_name);
}
//-------------
class MyDateClass2
{
//
private:
int m_year2; // Year
int m_month2;
public:
//--- Default constructor
MyDateClass2(void);
//--- A Parametric constructor
MyDateClass2(int m);
//--- A copying constructor
MyDateClass2(const MyDateClass2 &x){m_year2=x.m_year2;m_month2=x.m_month2;}
//------
int get2_y();
int get2_m();
};
MyDateClass2::MyDateClass2(void)
{
//---
MqlDateTime mdt;
datetime t=TimeCurrent(mdt);
m_year2=mdt.year;
// Print(__FUNCTION__);
// Print(x);
}
MyDateClass2::MyDateClass2(int m)
{
m_month2=11;
}
int MyDateClass2::get2_y()
{
return(m_year2);
}
int MyDateClass2::get2_m()
{
return(m_month2);
}
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
//Automatically Default constructor
MyDateClass1 mydate0;
mydate0.m_year1=2013;
mydate0.m_name="Sakata" ;
Print(mydate0.get1()," ",mydate0.gets());
//Automatically Default constructor and arry
MyDateClass1 mydate1[2];
mydate1[0].m_year1=2014;
mydate1[1].m_year1=2015;
Print(mydate1[0].get1()," ",mydate1[1].get1());
//Default constructor
MyDateClass2 mydate2;
MyDateClass2 mydate3();
//A parametric Default constructor
MyDateClass2 mydate4(11);
Print(mydate2.get2_y()," ",mydate3.get2_y()," ",mydate4.get2_m());
//A copying constructor
MyDateClass2 mydate5=mydate3;
MyDateClass2 mydate6(mydate4);
Print(mydate5.get2_y()," ",mydate6.get2_m());
}
//+------------------------------------------------------------------+
3、コンストラクタ2、デストラクタ、関数メソッド、新しいクラス
コンストラクタ2(初期化リスト)
1.初期化リストはコンストラクタでのみ使用できます。
2.初期化リストは関数の定義(実施)のとき行われます。
3.各メンバは、クラスメンバ(リスト)、の形で初期化されます。
4.初期化リストのメンバは任意の順でかまいませんが、
すべてメンバの表示順序(定義順序)に従って初期化されます。
5.表示の最初のメンバが初期化され、そして次が初期化されます。
6.順番は他のメンバとの評価による事を計算に入れておきましょう。
7.親メンバの初期化リストは初期化されないので、
8.べースクラスでデフォルトコンストラクタが定義されていないとき、
かつ一つあるいはそれ以上のコンストラクタが定義されているとき、
初期化リストでベーススクラスのコンストラクタの一つを常に呼び出さなければなりません。
9.初期化リストがどのようであっても、コンマで区切られたリストの通常のメンバの中で、
オブジェクトの初期化が始めに呼び出されます。
デストラクタ
1.デストラクタはクラスオブジェクトを消去するとき自動的に呼び出される特殊な関数です。
2.デストラクタは、名前の前にチルド(~)つけたクラス名で書きます。
3.デイニシャライゼーションが要求されるString、動的配列、オブジェクトは、
デストラクタがあるかないかに関わりなく、常にデイニシャライゼーションされます。
4.デストラクタがあればこれらの行為はデストラクタが呼び出された後に行われます。
5.キーワードvirtualが宣言されているかないかに関わらず、デストラクタは常にvirtualです。
(注:C++ではデストラクタは常にvirtualでない。)
関数メソッド
1.クラスの関数メソッドはクラスの内と外で定義されます。
2.クラス内でのメソッドの定義は、メソッドの定義の後ろ(右に)コードがきます。
3.クラスの外の関数のコードは、スコープレゾリュ-ションオペレータ(::)が使われます。
4.クラス名がスコープの前にきます。
新しいクラス
1.新しいクラスを開発は、メンバへの外からのアクセスの制限が求められます。
2.この目的の為に、private、protectedが使用されます。
3.この場合、同じクラスからのみアクセス出来るようにすることで、データを隠蔽します。
4.キーワードprotectedが使用されるときは、隠蔽データは継承クラスのメソッドからのアクセスも出来ます。
5.もしメンバのアクセスを完全にオープンにするときは、キーワードpublicを使用します。
6.public:の後に宣言されたクラスのメンバとメソッドは、プログラムによりクラスオブジェクトへ参照が可能となります。
7.private:の後に宣言された任意のメンバは、このクラスの関数でのみ可能となります。
8.常に後ろにコロン(:)が書かれ、何回でもクラス内で定義できます。
9.ベースクラスのメンバへのアクセスは派生クラスの継承で再定義されます。
下記プログラム
1.クラスCPerson1は、初期化リストのプログラミングの例です。
2.親クラスCParentと派生クラスCDerivedの初期化のときは、
始に派生クラスのderivedオブジェクトが生成され、
デフォルトコンストラクタCDerived()が呼び出されると、
ペアレントのCParentのコンストラクタが呼ばれます。
それからクラスメンバmemberのコンストラクタを行います。
/+------------------------------------------------------------------+
#property copyright "T.S"
#property link "http://"
#property version "1.00"
#property script_show_inputs
#property description "Constructors 2,destructor,Class function-methods and New class"
class CPerson1
{
string name1;
string name2;
string name3;
public:
//--- A constructor with an initialization list
CPerson1(string iname1,string iname2,string iname3):name2(iname2),name3(iname1),name1(iname3){ };
void PrintName(){Print(name1," ",name2," ",name3);};
};
//----------
class CParent
{
string name1;
public:
//--- A constructor with an initialization list
CParent(string name) : name1(name) { Print(name1);}
};
class CDerived : CParent
{
// A class member is an object of the parent
CParent member;
public:
//--- A default constructor in the initialization list calls the constructor of a parent
CDerived(): CParent("CDerived"), member(_Symbol) { };
};
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
CPerson1 peaple1("Asada","Baba","Cojima");
CPerson1 *peaple2 = new CPerson1("Kurokawa","Kida","Kaga");
//--- Output values
peaple1.PrintName();
peaple2.PrintName();
//--- Delete a dynamically created object
delete peaple2;
//---
CDerived derived;
}
//+------------------------------------------------------------------+
void、NULL定数、オブジェクトポインタ、値渡しと参照渡し、this
------------------------------------------------------------
voidタイプ
voidタイプは、構文的にほかのタイプと同じ基本タイプです。
関数が値を返さないとき、あるいはパラメータ(引数)のない事を意味する関数パラメータとして、
voidは使用されます。
ほかの基本タイプとは
(char uchar bool short ushort int uint color long ulong datetime float double string)
NULL定数
1.NULLは前もって定義された定数で、voidタイプです。
2.NULLのときは、変数に基本タイプの指定をしなくても、割り当てることが出来ます。
NULL値を持つ基本タイプの変数の時は認められます。
参考1
( if(some_string==NULL) some_string="empty"; )
3.NULLは、new operatorで生成されたオブジェクトのポインタとしても、割り当てることが出来ます。
参考2
(NULLポインタはnew operatorで生成されたオブジェクトのポインタと同じく
deleteで削除出来ます。NULLポインタは値として0を持ちます。)
オブジェクトポインタ
MQL5では、複雑な、ダイナミックオブジェクトを生成することが可能です。
これにはnew operatorが使われ、生成オブジェクトのデイスクリプタが戻されます。
デイスクリプタは8バイトのlargeです。
構文的にはMQL5のオブジェクトデイスクリプタはC++のポインタと同じです。
値渡しと参照渡し(関数)
MQL5では、シンプルタイプのパラメータは値渡しと参照渡しの両方が可能です。
混合タイプのパラメータも常に参照渡し出来ます。
参照渡しのパラメータは、アンパーサンド(&)をパラメータの名前の前に付けます。
this
リザーブワードthisはクラスのオブジェクトを参照するために使われます
クラス、構造体内部のメソッドから利用されます。
thisは常に、使用されるメソッドで参照されます。
式GetPointer(this)は、
関数メンバから呼ばれたGetPointer()の働きで、オブジェクトのポインタを与えます。
MQL5ではオブジェクトを返しませんが、オブジェクトのポインタを返します。
このように、オブジェクトを返すための関数が必要な時は、、
GetPointer(this)のように、このオブジェクトのポインタを返すことが出来ます。
参考3
(動的オブジェクトの関数メンバのメモリ)
オブジェクトはそのクラスで定義(動的定義のとき)されたデータメンバのコピーを含んでいますが、
メンバ関数のコードのコピーは、含まれていなません。
関数コードのコピーは、クラスオブジェクト全体で一つだけ別に、メモリに保存されます。
参考4
1.C++では変数に&を付けると、変数のアドレスを表します。
2.C++ではアドレスを格納するための変数がポインタになります。
int *s はint型の変数のアドレスを格納するポインタsを宣言しています。
しかし、間違えたメモリの使用は、PCのシステムを壊してしまうことになるので、
MQL5では、どちらも使用できません。
下記のプログラミングコードの例では、メソッドgetThis(void)は、
CThis *CThis::getThis(void)
{
return(GetPointer(this));
}
です。
少し複雑ですが、
メソッドの戻し値のタイプがクラスのポインタCThis *で、
スコープ名CThisの、
メソッド名getThis(void)の関数の定義が、
{ return(GetPointer(this)); }
となります。
//+------------------------------------------------------------------+
#property copyright "T.S"
#property link "http://"
#property version "1.00"
#property script_show_inputs
#property description "this,pointer"
class CThis
{
private:
double m;
public:
double get(void);
CThis *getThis();
};
double CThis::get(void)
{
return(m);
}
CThis *CThis::getThis(void)
{
return(GetPointer(this));
}
void OnStart()
{
//---
CThis *mythis;
mythis =new CThis;
string p1="";
string p2="";
if(CheckPointer(mythis.getThis())== POINTER_DYNAMIC)p1="Dynamic1";
else p1="No";
if(CheckPointer(mythis)== POINTER_DYNAMIC)p2="Dynamic2";
else p2="No";
Print(p1," ",p2);
delete mythis;
if(CheckPointer(mythis)== POINTER_DYNAMIC)p2="Dynamic2";
else p2="No";
Print(p2);
//---try !
/*
int a;
a=5;
//* is pointer cannot be used
int *b;
//& is operand expected
b=&a;
*/
}
//+------------------------------------------------------------------+
4、オブジェクト-オリエントプログラミング(OOP)の概要
オブジェクト-オリエントプログラミング(OOP)は、
主としてデータに焦点をあててプログラミングし、
データと動作は密接な関係にあります。
データと動作でクラスが構成され、オブジェクトはクラスのインスタンスになります。
オブジェクト-オリエントアプローチの構成要素は以下のようになります。
カプセル化と型拡張性
継承
多態性
オーバーロード(多重定義)
仮想関数
OOPは、動作モデルとして、コンピュータの操作を考えます。
モデル化されたアイテムは、抽象的なコンピュータの操作により表されたオブジェクトです。
よく知られているゲーム、テトリスを書きたい場合を考えてください。
そのためには、4個の正方形の角を互いに会わせ構成された、
ランダムな形の外観を、どのようにモデル化するのかを学ばなければなりません。
落下スピード、ローテーション操作の定義、そして形のシフト調整をする必要があります。
スクリーンでの形の動きは、縦穴の境界に制限されます。この制限はモデル化されなければなりません。
加えて、転がって落ちたキューブは破壊されて、獲得した得点がカウントされなければなりません。
このように理解のやさしいゲームは、
形モデル、縦穴(well)モデル、形移動モデル、その他いくつかのモデルを作る必要があります。
全てこれらのモデルは、コンピュータ計算で表現される抽象概念です。
これらのモデルを表現するために、抽象データタイプADT(あるいは複雑なデータタイプ)が使用されます。
厳密に言うと、DOMでの"shapes"動作モデルはデータタイプではありませんが、
"shapes"データタイプの操作の集合で、
制限された"well"データタイプを使用しています。
オブジェクトはクラス変数です。
オブジェクトオリエントプログラミングでは、簡単にADTを生成、使用できるようになります。
オブジェクトオリエントプログラミングは、継承メカニズムを使用しています。
継承は、すでにユーザによって定義されたデータタイプから得られた派生タイプです。
例えばテトリスの形を作るには、先にべースクラスShapeを作るのが便利です。
7つの形を表現するクラスは、そのベースクラスから派生し、
各々の形における動作の構成は、派生クラスで定義されます。
OOPでは、オブジェクトがそれらの動作に関する応答をします。
ADT開発者は、対応するオブジェクトで通常期待される、
動作を表すコードを含ませなければなりません。
オブジェクト自身がその動作の応答をするという事実は、
ユーザに関するオブジェクトプログラミングのタスクを大きく簡略化します。
もしスクリーンに形を描きたいなら、中心がどこにあるか、
それをどのように描くか知る必要があります。
もしseparate shapeが、drawするためどのようするか定義されているなら、
プログラマーは形を使用するとき、"draw"メッセージを送らなければなりません。
MQL5言語はC++に似ています。
そして、ADTを実装する隠蔽メカニズムを持ちます。
隠蔽は、内部の詳細の実装(*注釈:プライベート-メンバのこと)を扱える、
特殊なタイプを組み込む一方、
外部からアクセスでき、オブジェクトのこのタイプを扱える関数を組み込みます。
このタイプを使用するプログラムから、詳細の実装は受けつけません。
OOPのコンセプトは、以下に記載の、関連するコンセプトの集合です。
実世界における行為のシミュレーション
ユーザ定義のデータタイプ
実施する詳細の隠蔽
継承によりコードの再利用が可能
実行中のファンクションコールの解読
これらコンセプトの幾つかはむしろあいまいで、幾つかは抽象的、他は一般的です。
ADTの例として、MQL5スタンダードライブラリーのベースクラスであるCObjectがあります。
CObjectクラスはObject.mqhのファイルにあり、
MeTaEditorの左にあるNavigatorのIncludeデイレクトリーに保管されています。
CObjectは、MeTaEditorに格納されているMQL5スタンダードライブラリーの、
ほとんどのベースクラスとして使われおり、特に重要なクラスです。
学習の参考のため、CObjectのプログラムコードを以下に載せてあります。
(変更、手直し等の手は一切加えておりません。)
C++に比べると、機能はシンプルです。
オブジェクトの記憶にlistを使うのは、
メモリーの使用を少なくするためと、
listが配列より扱いやすいためです。
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Object.mqh |
//| Copyright 2009-2013, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#include "StdLibErr.mqh"
//+------------------------------------------------------------------+
//| Class CObject. |
//| Purpose: Base class for storing elements. |
//+------------------------------------------------------------------+
class CObject
{
private:
CObject *m_prev; // previous item of list
CObject *m_next; // next item of list
public:
CObject(void): m_prev(NULL),m_next(NULL) { }
~CObject(void) { }
//--- methods to access protected data
CObject *Prev(void) const { return(m_prev); }
void Prev(CObject *node) { m_prev=node; }
CObject *Next(void) const { return(m_next); }
void Next(CObject *node) { m_next=node; }
//--- methods for working with files
virtual bool Save(const int file_handle) { return(true); }
virtual bool Load(const int file_handle) { return(true); }
//--- method of identifying the object
virtual int Type(void) const { return(0); }
//--- method of comparing the objects
virtual int Compare(const CObject *node,const int mode=0) const { return(0); }
};
//+------------------------------------------------------------------+
5、オブジェクト-オリエントプログラミング(OOP)の概要
型のカプセル化と型拡張性
------------------------------------------------------------
OOPはソフトウエアを書くためのバランスの取れたアプローチです。
データとビヘイビア(振る舞い)は共にパックされます。
カプセル化により、ユーザ定義のデータタイプを生成し、
言語のデータタイプを拡張し、そしてお互いに影響し合います。
幸いにも、型拡張性により言語のユーザ定義データタイプがさらに加えられ、
基本タイプ(訳注:char、int、double等)と同様、簡単に使えるようになります。
抽象データタイプ、例えば、究極の表現では、
よく知られたビヘイビアタイプの、あるデータ列(string)。
データ列(string)のユーザは、stringの操作が、
連結(データ列の)やprint(データ列の出力)のような、
確実なビヘイビアを持つことを知っています。
連結やprintの操作はメソッドと呼ばれています。
ADT(抽象データタイプ)の確実な実装は、幾つかの制限があります。
例えば、長さに制限のあるデータ列(string)。
これらの制限は、パブリックなビヘイビアに影響を与えます。
しかし、内部あるいはプライベートなインプリメンテーションの詳細は、
ユーザが見ているオブジェクトの進行に、直接影響を与えません。
(訳注:C++の設計では、インプリメンテーションはプライベートメンバー、
パブリックメンバーはインターフェイスのこと。)
例えば、データ列(string)はしばしば配列として実装されますが、
この配列の内部でのベーシックなアドレスと、
その名前はユーザにとって必要ではありません。
(訳注:配列のほかにはリストがあります。
アドレスや名前は、クラスのプライベートなメンバとなります。)
ユーザ定義タイプのオープン-インターフェイスを提供する場合、
カプセル化はインプリメンテーションの詳細を隠すことが出来ます。
C++と同様 MQL5では、
キーワードprivate、protected、publicと組み合わせて、
クラスと構造体の定義(classとstruct)によるカプセル化に使われます。
publicキーワードにより、隠れているメンバーにアクセス出来、
無制限にオープンとなります。
このキーワードが無いと、クラスのメンバはデフォルトではロックされます。
プライベートメンバは、そのクラスからとメンバ関数からのみアクセス出来ます。
プロテクトのクラス関数は、そのクラスと、継承クラスからアクセスが可能です。
パブリックなクラス関数は、クラスのスコープ内での宣言で使用可能となります。
プロテクションは、
データ構造を変えることが予め期待されていない、
クラスの実装の一部を、隠す事を可能にします。
アクセス制限、データ隠蔽はオブジェクト-オリエントプログラミングの特色となっています。
6、継承 1
OOPの典型的な特徴は、継承によるコードの再利用の推進です。
新しいクラスは、いま在るベースックラスと呼ばれるものから作られます。
派生クラスはベースクラスのメンバーを使用し、
それを修正し、補う事が出来ます。
沢山の型は、存在する型からのヴァリエーションとなります。
それら新しいコードの開発は、しばしば退屈です。
さらに、新しいコードはエラーを暗示します。
派生クラスはベースクラスの定義説明を継承し、
さらにコードの再開発とテストが必要です。
継承関係は階層的です。
階層は、全て多様、複雑で、要素をコピーすることが認められています。
それはオブジェクトを分類します。
例えば、元素の周期表の中には気体もありますが、
(訳注:ヘリウムガス、ネオンガス、アルゴンガス等)
それは、周期元素の全てに本来備わっている属性です。
ガスは次のシステムの一部になります。
不活性ガスは、次の重要なサブクラス(下位分類)を与えます。
不活性ガスの次の階層は、アルゴンのようなガスです。
階層は、明らかに不活性ガスの振る舞いとして解釈することが出来ます。
私達はそれらの原子が陽子と電子から成り立ていることを知っています。
ほかの全ての元素に関しても同じです。他のガスも同様です。
他の元素との通常の化学的な結合をするガスは、
不活性ガスのサブクラスではありません。
幾何学的形状の継承の例を考えてみましょう。
簡単な形(円、三角形、長方形、正方形、その他)の多様なバラエテイを描くには、
すべての派生クラスの原型、ベストな方法である、
ベースクラス(ADT)を生成することが大事です。
ベースクラスCShapeを作ってみましょう。
形を描くための最も共通するメンバを持たせます。
これらのメンバは、ある形の特徴的なプロパテイ、形のタイプ、主要な固定点の座標となります。
//--- The base class Shape
class CShape
{
protected:
int m_type; // Shape type
int m_xpos; // X - coordinate of the base point
int m_ypos; // Y - coordinate of the base point
public:
CShape(){m_type=0; m_xpos=0; m_ypos=0;} // constructor
void SetXPos(int x){m_xpos=x;} // set X
void SetYPos(int y){m_ypos=y;} // set Y
次に、ベースクラスから新しい派生クラスの生成しますが、
それは必要な面の広がりを加えた、特定のクラスを作ります。
円形に関しては、半径の値を持つメンバを加える必要があります。
正方形は辺の値により特徴付けられます。
ベースクラスCShapeから継承した派生クラスは次のようになります。
//--- The derived class circle
class CCircle : public CShape // After a colon we define the base class
{ // from which inheritance is made
private:
int m_radius; // circle radius
public:
CCircle(){m_type=1;} // constructor, type 1
};
正方形クラスの宣言も同様です。
//--- the derived class Square
class CSquare : public CShape // After a colon we define the base class
{ // from which inheritance is made
private:
int m_square_side; // square side
public:
CSquare(){m_type=2;} // constructor, type 2
};
始めにベースクラスのコンストラクタが呼ばれ、
次に派生クラスのコンストラクタ呼ばれ、
オブジェクトが生成されることに注意してください。
オブジェクトが消去されるときは、
始めに派生クラスのデストラクタが呼ばれ、
次にベースクラスのデストラクタが呼ばれます。
このように、最も一般的なベースクラスのメンバを宣言することで、
特殊なクラスである派生クラスにメンバを更に加えることが出来ます。
継承は何回も再使用できるパワフルなライブラリコードを生成することが出来ます。
+------------------------------------------------------------------------+