Diskussion zum Artikel "Verwendung von Objektzeigern in MQL5" - Seite 3

 
In einem solchen Fall sollten Sie keine Zeiger verwenden.
 
Barbarian2:

Ich verstehe immer noch nicht, über Zeiger und Referenzen in MQL5 und jetzt in MQL4. Was ist der Unterschied zwischen der Übergabe von Referenzen und Zeigern, außer dem zusätzlichen Code? In C++ gibt es einen Unterschied, aber was ist er hier? Wenn es nicht schwierig ist, mehr Details zu schreiben.

Die Übergabe per Referenz erfordert, dass das Objekt, das per Referenz übergeben wird, initialisiert wird. Bei der Übergabe per Zeiger gibt es diese Einschränkung nicht:

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);
}

Wenn die Funktion TestShapeRef aufgerufen wird, stürzt der Code ab, weil die Form nicht initialisiert ist. In der Funktion TestShapePointer hingegen ist eine ständige Überprüfung erforderlich, ob das übergebene Objekt initialisiert ist oder nicht. Befolgen Sie daher die imprirische Regel:

Wenn gewährleistet sein muss, dass das Objekt zum Zeitpunkt der Übergabe an die Funktion existiert, verwenden Sie einen "&"-Übertrag. Wenn die Funktion ein Objekt benötigt, dessen Zustand undefiniert ist - verwenden Sie die Übergabe mit einem "*"-Zeiger, und überprüfen Sie innerhalb dieser Funktion die Gültigkeit des übergebenen Zeigers.

Es gibt noch eine weitere Nuance, die Sie im Auge behalten sollten. Betrachten Sie das vorherige Beispiel:

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);
}
Wird dieses Programm korrekt funktionieren? Nein, es wird mit der Fehlermeldung "invalid pointer access" in der Zeile printf(shape.name); //ERROR (!?) enden, obwohl wir scheinbar garantiert ein Objekt in der Funktion TestShapePointer erzeugen. Der Punkt ist, dass tatsächlich eine NULL-Referenz anstelle von shape übergeben wurde. Das heißt, die Form innerhalb der Funktion und die übergebene Form sind unterschiedliche Objekte! Daher ist nach dem Verlassen der Funktion shape immer noch gleich NULL, und der shape-Zeiger innerhalb der Funktion ist verloren (auf dem Stack gelöscht). Also.
 

Ist es möglich, ein Array mit Objekten verschiedener Typen zu erstellen?

Nehmen wir dieses Beispiel:

//+------------------------------------------------------------------+
//|#Test.mq5 |
//| Copyright 2014, MetaQuotes Software Corp. | |
//|http://www.mql5.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[];
//+------------------------------------------------------------------+
//| Skript-Programmstartfunktion|
//+------------------------------------------------------------------+
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);
//---
   //objects[0]. // Wie erhält man Zugriff auf Methoden der Klasse CChartObjectEdit?
   //objects[1]. // Wie erhält man Zugriff auf Methoden der Klasse CChartObjectButton?
   //objects[2]. // Wie erhält man Zugriff auf Methoden der Klasse CChartObjectRectLabel?
//---
   while(!IsStopped()) Sleep(1000);
  }
//+------------------------------------------------------------------+
//| Gibt die Anzahl der Objekte zurück|
//+------------------------------------------------------------------+
int ElementsTotal()
  {
   return(ArraySize(objects));
  }
//+------------------------------------------------------------------+
//|| Fügt ein Objekt zum Array hinzu|
//+------------------------------------------------------------------+
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()));
  }
//+------------------------------------------------------------------+
//| Übersetzt den Objekttyp in eine Zeichenkette|
//+------------------------------------------------------------------+
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");
  }
//+------------------------------------------------------------------+

//---

Wie erhält man Zugriff auf Methoden geerbter Klassen?

 
tol64:

Wie kann man auf Methoden von Vererbungsklassen zugreifen?

Haben Sie versucht, auf den Zieltyp zu casten?
 
Rosh:
Haben Sie versucht, einen Zieltyp zu werfen?
Nein. Das ist das erste Mal, dass ich davon höre. Wo kann ich darüber lesen?
 
tol64:
Nein. Das ist das erste Mal, dass ich davon höre. Wo kann ich darüber lesen?
Es ist ein gängiges Adjektiv, hier ist ein Beispiel:

//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
class CFoo
  {
  };
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
class CBar : public CFoo
  {
public:
   void func(int x) { Print("Hello from Bar ",x); }
  };
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
void func(CFoo *foo)
  {
//--- Aufruf mit Casting direkt vom Zeiger zum Basistyp
   ((CBar*)foo).func(1);
//--- Aufruf mit Konvertierung in den Zieltyp und Speichern einer Kopie des Zeigers mit dem gewünschten Typ
   CBar *b=(CBar *)foo;
   b.func(2);
//--- die dynamisch erstellte Klasse löschen
   delete foo;
  }
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
void OnStart()
  {
   func(new CBar);
  }
//+------------------------------------------------------------------+
 
mql5:
Die übliche Typumwandlung, hier ein Beispiel:

Ich danke Ihnen. Ich werde es mir ansehen.
 
Rosh:
Haben Sie versucht, auf den Zieltyp zu casten?
Scheiße, und danach reden Sie über Sprachsicherheit?
 
TheXpert:
Scheiße, und danach reden Sie über Sprachsicherheit?
Hast du eine Sicherheitslücke gefunden? )
 

Wäre es nicht besser, Polymorphismus zu verwenden?

Es geht ungefähr so:

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;