記事"MQL5でのオブジェクトポインターの使用"についてのディスカッション - ページ 3

 
このような場合、ポインターは使わないこと。
 
Barbarian2:

MQL5とMQL4のポインタと参照についてまだ理解していません。参照渡しとポインターの違いは、余分なコードを除いて何なのでしょうか?C++では違いがありますが、ここではどうなのでしょうか?もっと詳しく書くのが難しくなければ。

参照渡しでは、参照渡しされたオブジェクトを初期化する必要があります。ポインタ渡しにはこの制約がない:

class CShape
{
   public:
      string name;
};

void OnInit()
{
   CShape* shape = NULL; 
   TestShapePointer(shape);
   //Critical Error!
   TestShapeRef(shape);
   
}

void TestShapeRef(CShape& shape)
{
   printf(shape.name);
}

void TestShapePointer(CShape* shape)
{
   if(CheckPointer(shape) != POINTER_INVALID)
      printf(shape.name);
}

TestShapeRef関数を呼び出すと、シェイプが初期化されていないので、コードはクラッシュする。一方、TestShapePointer関数の内部では、渡されたオブジェクトが初期化されているかどうかを常にチェックする必要があります。従って、非経験的なルールに従ってください:

オブジェクトが関数に渡される時点で存在することが保証されている必要がある場合は、"&"パスを使用します。オブジェクトの状態が未定義の場合は、"*"ポインタ渡しを使い、関数内で渡されたポインタの有効性をチェックする。

もうひとつ、微妙なニュアンスの違いを覚えておく必要がある。先ほどの例を考えてみよう:

class CShape
{
   public:
      string name;
};

void OnInit()
{
   CShape* shape = NULL; 
   TestShapePointer(shape);
   printf(shape.name); //ERROR (!?)
}

void TestShapePointer(CShape* shape)
{
   if(CheckPointer(shape) == POINTER_INVALID)
      shape = new CShape();
   printf(shape.name);
}
このプログラムは正しく動くだろうか?いや、TestShapePointer関数でオブジェクトの生成が保証されているように見えるにもかかわらず、printf(shape.name); //ERROR(!?)という行で "invalid pointer access "というエラーで終わってしまうだろう。ポイントは、実際にはshapeの代わりにNULL参照が渡されたということです。つまり、関数内のshapeと渡されたshapeは別の オブジェクトなのです!したがって、関数を終了した後もshapeはNULLのままであり、関数内のshapeポインタは失われている(スタックでクリアされている)。つまり
 

異なる型のオブジェクトで配列を作ることは可能ですか?

次の例を見てみましょう:

//+------------------------------------------------------------------+
//|#テストmq5
//| Copyright 2014, MetaQuotes Software Corp.
//|http://mql5.commql5.com
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
//---
#include <ChartObjects\ChartObjectsTxtControls.mqh>
//---
CChartObjectRectLabel rect_label;
CChartObjectButton    button;
CChartObjectEdit      edit;
//---
CChartObject *objects[];
//+------------------------------------------------------------------+
//| スクリプト番組開始機能|
//+------------------------------------------------------------------+
void OnStart()
  {
   edit.Create(0,"edit_1",0,20,20,100,20);
   button.Create(0,"button_1",0,20,40,100,20);
   rect_label.Create(0,"rect_label_1",0,20,60,100,20);
//---
   AddObjectToArray(edit);
   AddObjectToArray(button);
   AddObjectToArray(rect_label);
//---
   //オブジェクト[0]。// CChartObjectEditクラスのメソッドにアクセスするには?
   //オブジェクト[1]。// CChartObjectButtonクラスのメソッドにアクセスするには?
   //オブジェクト[2]。// CChartObjectRectLabelクラスのメソッドにアクセスするには?
//---
   while(!IsStopped()) Sleep(1000);
  }
//+------------------------------------------------------------------+
//| オブジェクトの数を返す|
//+------------------------------------------------------------------+
int ElementsTotal()
  {
   return(ArraySize(objects));
  }
//+------------------------------------------------------------------+
//| 配列にオブジェクトを追加する|
//+------------------------------------------------------------------+
void AddObjectToArray(CChartObject &object)
  {
   int size=ElementsTotal();
//---
   ArrayResize(objects,size+1);
   objects[size]=GetPointer(object);
//---
   if(CheckPointer(objects[size])!=POINTER_INVALID)
      Print(__FUNCSIG__," >>> array["+IntegerToString(size)+"]: ",objects[size].Name(),
            "; object type: ",ObjectTypeToString(objects[size].Type()));
  }
//+------------------------------------------------------------------+
//| オブジェクト・タイプを文字列に変換する。|
//+------------------------------------------------------------------+
string ObjectTypeToString(int type)
  {
   string str="";
//---
   switch((ENUM_OBJECT)type)
     {
      case OBJ_EDIT            : return("edit");            break;
      case OBJ_BUTTON          : return("button");          break;
      case OBJ_RECTANGLE_LABEL : return("rectangle label"); break;
     }
//---
   return("undefined object type");
  }
//+------------------------------------------------------------------+

//---

継承されたクラスのメソッドにアクセス するには?

 
tol64:

継承クラスのメソッドにアクセス するには?

対象の型にキャストしてみましたか?
 
Rosh:
ターゲットタイプをキャストしてみましたか?
いいえ、初めて聞きました。どこで読むことができますか?
 
tol64:
いいえ、初めて聞きました。どこで読むことができますか?
よくあるタイプの形容詞 です:

//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
class CFoo
  {
  };
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
class CBar : public CFoo
  {
public:
   void func(int x) { Print("Hello from Bar ",x); }
  };
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
void func(CFoo *foo)
  {
//--- ポインタから基底型への直接キャストによる呼び出し
   ((CBar*)foo).func(1);
//--- ターゲット型に変換して呼び出し、必要な型を持つポインタのコピーを保存する。
   CBar *b=(CBar *)foo;
   b.func(2);
//--- 動的に作成されたクラスを削除する
   delete foo;
  }
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
void OnStart()
  {
   func(new CBar);
  }
//+------------------------------------------------------------------+
 
mql5:
通常のタイプ変換の 例です:

ありがとうございます。調べてみます。
 
Rosh:
対象の型にキャストしてみた?
くそっ、そのあとで言語安全性について語るのか?
 
TheXpert:
くそっ、そのあとで言語セキュリティの話か?
セキュリティホールを見つけたのか?)
 

ポリモーフィズムを 使った方がいいのでは?

それは次のようなものだ:

CChartObject *ptr_rect_label;
CChartObject *ptr_button;
CChartObject *ptr_edit;
CChartObject *objects[];
//---
ptr_rect_label=new CChartObjectRectLabel;
ptr_button=new CChartObjectButton;
ptr_edit=new CChartObjectEdit;