Fragen zu OOP (Objektorientierte Programmierung) - Seite 10

 

Parameternamen sind nicht wichtig... verschiedene Namen sind sinnvoll, damit etwas nicht mit etwas verwechselt wird...

Sie können einige Werte in die Funktionsdeklaration schreiben,

public:
   void              SetName(string n);

und andere Werte in der Funktion selbst

void CPerson::SetName(string nnn)
  {
   m_name.first_name=GetFirstName(nnn);
   m_name.last_name=GetLastName(nnn);
  }

oder die Parameter können überall gleich benannt werden, je nachdem, was für den Codewriter bequemer ist

 

In demselben Lehrbuch wurde der Code gefunden:

#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Класс-пример с несколькими типами доступа                        |
//+------------------------------------------------------------------+
class CBaseClass
  {
private:             //--- закрытый член недоступен из потомков
   int               m_member;
protected:           //--- защищенный метод доступен из базового класса и его потомков
   int               Member(){return(m_member);}
public:              //--- конструктор класса доступен всем
                     CBaseClass(){m_member=5;return;};
private:             //--- закрытый метод для присвоения значения члену m_member
   void              Member(int value) { m_member=value;};
  };
//+------------------------------------------------------------------+
//| Производный класс с ошибками                                     |
//+------------------------------------------------------------------+
class CDerived: public CBaseClass // public наследование можно не указывать, оно по умолчанию
  {
public:
   void Func() // определим в потомке функцию с обращениями к членам базового класса 
     {
      //--- попытка модификации закрытого члена базового класса
      m_member=0;        // ошибка, закрытый член базового класса никому не доступен
      Member(0);         // ошибка, закрытый метод базового класса не доступен в потомках

Seltsame Sache mit dem Konstruktor:

public:              //--- конструктор класса доступен всем
                     CBaseClass(){m_member=5;return;};
Warum gibt es hier einen Return-Operator?

Dies ist das erste Mal, dass ich diesen Operator im Konstruktor verwendet sehe. Der Konstruktor wird nämlich automatisch aufgerufen. Und eine Ausgabe wird es sowieso geben. Ist dieser Operator im Konstruktor sinnvoll?

 
hoz:

In demselben Lehrbuch wurde der Code gefunden:

Seltsame Sache mit dem Konstruktor:

Warum gibt es hier einen Return-Operator?

Dies ist das erste Mal, dass ich diesen Operator im Konstruktor verwendet sehe. Der Konstruktor wird nämlich automatisch aufgerufen. Und eine Ausgabe wird es sowieso geben. Ist dieser Operator im Konstruktor sinnvoll?

In diesem Beispiel wird sie nicht benötigt, aber es kann eine komplexe Initialisierung geben, wenn ein frühzeitiges Verlassen erforderlich ist.

Der Konstruktor und der Destruktor sind normale Funktionen. Nur der Standardkonstruktor und -destruktor werden automatisch aufgerufen. Die anderen werden vom Benutzer aufgerufen.

 

Im Lehrbuch wird dieses Beispiel im Zusammenhang mit Polymorphismus genannt:

//--- Базовый класс
class CShape
  {
protected: 
   int            m_type;                // тип фигуры
   int            m_xpos;                // X - координата точки привязки
   int            m_ypos;                // Y - координата точки привязки
public:
   void           CShape(){m_type=0;};   // конструктор, тип равен нулю
   int            GetType(){return(m_type);};// возвращает тип фигуры
virtual
   double         GetArea(){return (0); }// возвращает площадь фигуры
  };
//--- производный класс Круг
class CCircle : public CShape            // после двоеточия указывается базовый класс,
  {                                      // от которого производится наследование 
private:
   double         m_radius;              // радиус круга
public:
   void           CCircle(){m_type=1;};  // конструктор, тип равен 1 
   void           SetRadius(double r){m_radius=r;};
   virtual double GetArea(){return (3.14*m_radius*m_radius);}// площадь круга
  };
class CSquare : public CShape            // после двоеточия указывается базовый класс,
  {                                      // от которого производится наследование 
private:
   double          m_square_side;        // сторона квадрата
public:
   void            CSquare(){m_type=2;}; // конструктор, тип равен 2 
   void            SetSide(double s){m_square_side=s;};
   virtual double  GetArea(){return (m_square_side*m_square_side);}//площадь квадрата
  };

Es gibt eine Sache, die ich nicht verstehe. Wenn wir Child-Funktionsobjekte für Aufrufe verwenden, d.h. abgeleitete Methoden CCircle und CSquare, dann kann GetArea() area unter Umgehung der Deklarationen in der Basisklasse berechnet werden. D.h. erstellen Sie in einer Basisklasse überhaupt keine virtuellen Funktionen, und erstellen Sie in abgeleiteten Methoden eine reguläre Methode und das war's! Warum brauchen wir also eine virtuelle Funktion?

Es ist interessant, ein adäquates und logisches Beispiel zu sehen, an dem man erkennen kann, dass virtuelle Funktionen einen gewissen Nutzen bringen. Denn was ich gesehen habe, war nicht logisch, zumindest für mich. Ich würde es trotzdem gerne verstehen.

 
hoz:

Im Lehrbuch wird dieses Beispiel im Zusammenhang mit Polymorphismus genannt:

Es gibt eine Sache, die ich nicht verstehe. Wenn wir Child-Funktionsobjekte für Aufrufe verwenden, d.h. abgeleitete Methoden CCircle und CSquare, dann kann GetArea() area unter Umgehung der Deklarationen in der Basisklasse berechnet werden. D.h. erstellen Sie in einer Basisklasse überhaupt keine virtuellen Funktionen, und erstellen Sie in abgeleiteten Methoden eine einfache Methode und das war's! Warum brauchen wir also eine virtuelle Funktion?

Es ist interessant, ein adäquates und logisches Beispiel zu sehen, an dem man erkennen kann, dass virtuelle Funktionen einen gewissen Nutzen bringen. Denn was ich gesehen habe, war nicht logisch, zumindest für mich. Ich würde es trotzdem gerne verstehen.

Dies ist das einfachste Beispiel für das Verständnis von Polymorphismus. Um es schnell zu bekommen.

Es gibt komplizierte Fälle. Sie werden es anwenden, wenn Sie es brauchen. Es hat keinen Sinn, sich jetzt zu bemühen. Wenn die Aufgabe erledigt ist, werden Sie darüber nachdenken müssen.

Ich habe zum Beispiel eine Basisklasse mit allen möglichen Lese-/Schreibschnittstellen. Sie hat auch private virtuelle Methoden (insgesamt 2 - Lesen/Schreiben), die diese Schnittstelle in der Basisklasse mit abgeleiteten Klassen verbinden. Eigentlich können die abgeleiteten Klassen überall dort sein, wo mit Dateien gearbeitet wird (Dateien, Mapping, Kanäle, Internet). Jede der abgeleiteten Klassen definiert diese virtuellen Methoden anders, aber alle Klassen haben die gleiche Schnittstelle zur Basisklasse.

 
hoz:

Im Lehrbuch wird dieses Beispiel im Zusammenhang mit Polymorphismus genannt:

Es gibt eine Sache, die ich nicht verstehe. Wenn wir Child-Funktionsobjekte für Aufrufe verwenden, d.h. abgeleitete Methoden CCircle und CSquare, dann kann GetArea() area unter Umgehung der Deklarationen in der Basisklasse berechnet werden. D.h. keine virtuellen Funktionen in der Basisklasse erstellen, und in abgeleiteten Methoden eine einfache Methode erstellen und das war's! Warum brauchen wir also eine virtuelle Funktion?

Es ist interessant, ein adäquates und logisches Beispiel zu sehen, an dem man erkennen kann, dass virtuelle Funktionen einen gewissen Nutzen bringen. Denn was ich gesehen habe, war nicht logisch, zumindest für mich. Ich möchte es trotzdem verstehen.

Ich werde versuchen, ein kleines Beispiel zu skizzieren:

#property strict
#property show_inputs

enum Mes_type {
    m1,
    m2
};
input Mes_type mes_t;  // Выберите тип сообщения

class Message {
public:
    virtual void action() {};
};

class Mes1 : public Message {
public:
    virtual void action() {Alert("Типичные ошибки в программах");}
};

class Mes2 : public Message {
public:
    virtual void action() {Alert("Оффлайновые графики");}
};

void OnStart() {
    // Формируем входные данные для какого-то алгоритма
    //////////////////////////////////////////
    Message *mes;                           //
    switch(mes_t)                           //
    {                                       //
        case m1:                            //
            mes = new Mes1;                 //
            break;                          //
        case m2:                            //
            mes = new Mes2;                 //
    }                                       //
    /////////////////////////////////////////
    
    // Рабочий алгоритм
    //////////////////////////////////////////
    mes.action();                           //
    //////////////////////////////////////////
  
    delete mes;
}

Dank dieser Struktur müssen wir nicht in den Arbeitsalgorithmus einsteigen, der sehr umfangreich und komplex sein kann (hier ist alles vereinfacht), wir müssen nur einen weiteren Nachkommen, m3 in der Aufzählung und einen weiteren Fall in switch hinzufügen. Das heißt, wir haben die Eingabedaten vereinheitlicht, wodurch eine Bearbeitung im Hauptteil des Programms vermieden wird.

Dies ist natürlich nur dann sinnvoll, wenn der Arbeitsalgorithmus eine Vielzahl von Typen als Eingabe zulässt. Wenn es nur einen Typ gibt, ist all dies nutzlos.

 
hoz:

Im Lehrbuch wird dieses Beispiel im Zusammenhang mit Polymorphismus genannt:

Es gibt eine Sache, die ich nicht verstehe. Wenn wir Child-Funktionsobjekte für Aufrufe verwenden, d.h. abgeleitete Methoden CCircle und CSquare, dann kann GetArea() area unter Umgehung der Deklarationen in der Basisklasse berechnet werden. D.h. erstellen Sie in einer Basisklasse überhaupt keine virtuellen Funktionen, und erstellen Sie in abgeleiteten Methoden eine einfache Methode und das war's! Warum brauchen wir also eine virtuelle Funktion?

Es ist interessant, ein adäquates und logisches Beispiel zu sehen, an dem man erkennen kann, dass virtuelle Funktionen einen gewissen Nutzen bringen. Denn was ich gesehen habe, war nicht logisch, zumindest für mich. Ich würde es trotzdem gerne verstehen.

Hier ist ein einfaches Beispiel:

CShape* GetNewShape()
{
        if( ... ) // Здесь какое-то условие.
                return new CCircle();
        else
                return new CSquare();
}

CShape* M[10];

for( int i = 0; i < 10; i++ )
{
        M[i] = GetNewShape();
}


double WholeArea = 0.0;
for( int i = 0; i < 10; i++ )
{
        WholeArea += M[i].GetArea();
}
Wir verwenden die Funktion GetArea(), ohne zu wissen, für welche Form sie aufgerufen wird.
 

Ich habe diesen Setter in einer Klasse:

//---- SetColorBySend
TradingFunc::SetColorBySend (const color fc_ColorSendBuy,      // Цвет открытия ордера на покупку
                             const color fc_ColorSendSell)     // Цвет открытия ордера на продажу
{
   ColorBySend [2] = {fc_ColorSendBuy, fc_ColorSendSell};
}

Der Compiler bekämpft diese Zuweisung von Elementen an das ColorBySend-Array im Allgemeinen wie folgt:

'fc_ColorSendBuy' - constant expression required        TradingFunc.mqh 91      23
'fc_ColorSendSell' - constant expression required       TradingFunc.mqh 91      40
'{' - expression expected       TradingFunc.mqh 91      22
Womit hat das zu tun? Ist es wirklich notwendig, die Werte Element für Element zuzuweisen? Ist es nicht möglich, dies als Liste zu tun? Worauf bezieht sie sich? Schließlich wird die Zuweisung auch im Lehrbuch so vorgenommen...
 
hoz:

Ich habe diesen Setter in einer Klasse:

Der Compiler schwört auf diese Zuweisung von Elementen an das ColorBySend-Array im Allgemeinen wie folgt:

Womit hat das zu tun? Ist es wirklich notwendig, die Werte Element für Element zuzuweisen? Ist es nicht möglich, dies als Liste zu tun? Was hat das damit zu tun? Schließlich werden die Aufgaben auch im Lehrbuch so gestellt...


Ein Konstrukt der Art {irgendetwas, etwas anderes} kann nur ein Array von Konstanten sein. Allerdings werden dort Parameter ersetzt, die in die Funktion übergeben werden und eigentlich lokale Variablen der Funktion sind. Der Modifikator const hat in diesem Fall keine Bedeutung, da er nur angibt, dass der übergebene Wert nicht geändert werden kann. Infolgedessen ist
{fc_ColorSendBuy, fc_ColorSendSell}
ein Variablenausdruck, den der Compiler nicht verstehen kann. Leider.
 
Die Initialisierung durch eine Liste ist nur mit einer Deklaration möglich.