Questions sur la POO (programmation orientée objet) - page 10

 

les noms des paramètres ne sont pas importants... des noms différents ont un sens afin que quelque chose ne soit pas confondu avec quelque chose...

Vous pouvez écrire certaines valeurs dans la déclaration de la fonction,

public:
   void              SetName(string n);

et d'autres valeurs dans la fonction elle-même

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

ou vous pouvez nommer les paramètres de manière identique partout, selon ce qui est le plus pratique pour l'auteur du code.

 

Dans le même manuel, le code est apparu :

#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);         // ошибка, закрытый метод базового класса не доступен в потомках

Une chose étrange à propos du constructeur :

public:              //--- конструктор класса доступен всем
                     CBaseClass(){m_member=5;return;};
Pourquoi y a-t-il un opérateur de retour ici ?

C'est la première fois que je vois cet opérateur utilisé dans le constructeur. En fait, le constructeur est appelé automatiquement. Et il y aura une sortie de toute façon. Cet opérateur a-t-il un sens dans le constructeur ?

 
hoz:

Dans le même manuel, le code est apparu :

Une chose étrange à propos du constructeur :

Pourquoi y a-t-il un opérateur de retour ici ?

C'est la première fois que je vois cet opérateur utilisé dans le constructeur. En fait, le constructeur est appelé automatiquement. Et il y aura une sortie de toute façon. Cet opérateur a-t-il un sens dans le constructeur ?

Elle n'est pas nécessaire dans cet exemple, mais il peut y avoir une initialisation complexe lorsque la sortie anticipée est nécessaire.

Le constructeur et le destructeur sont des fonctions normales. Seuls le constructeur et le destructeur par défaut sont appelés automatiquement. Les autres sont appelés par l'utilisateur.

 

Le manuel donne cet exemple en relation avec le polymorphisme :

//--- Базовый класс
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);}//площадь квадрата
  };

Il y a une chose que je ne comprends pas. Si nous utilisons des objets de fonction enfant pour les appels, c'est-à-dire les méthodes dérivées CCircle et CSquare, alors la zone GetArea() peut être calculée en contournant les déclarations de la classe de base. C'est-à-dire ne pas créer du tout de fonctions virtuelles dans une classe de base, et dans les méthodes dérivées créer une simple méthode et c'est tout ! Alors pourquoi avons-nous besoin d'une fonction virtuelle ?

Il est intéressant de voir un exemple adéquat et logique, où l'on peut constater que les fonctions virtuelles apportent un certain avantage. Parce que ce que j'ai vu n'était pas logique, du moins pour moi. J'aimerais tout de même le comprendre.

 
hoz:

Le manuel donne cet exemple en relation avec le polymorphisme :

Il y a une chose que je ne comprends pas. Si nous utilisons des objets de fonction enfant pour les appels, c'est-à-dire les méthodes dérivées CCircle et CSquare, alors la zone GetArea() peut être calculée en contournant les déclarations de la classe de base. C'est-à-dire ne pas créer du tout de fonctions virtuelles dans une classe de base, et dans les méthodes dérivées créer une simple méthode et c'est tout ! Alors pourquoi avons-nous besoin d'une fonction virtuelle ?

Il est intéressant de voir un exemple adéquat et logique, où l'on peut constater que les fonctions virtuelles apportent un certain avantage. Parce que ce que j'ai vu n'était pas logique, du moins pour moi. J'aimerais tout de même le comprendre.

Il s'agit de l'exemple le plus simple pour comprendre le polymorphisme. Pour l'obtenir rapidement.

Il y a des cas compliqués. Vous l'appliquerez quand vous en aurez besoin. Il n'y a pas de raison de s'embêter maintenant. Une fois la tâche accomplie, vous devrez y réfléchir.

Par exemple, j'ai une classe de base avec toutes les interfaces de lecture/écriture possibles. Elle possède également des méthodes virtuelles privées (2 au total - lecture/écriture), qui relient cette interface dans la classe de base avec les classes dérivées. En fait, les classes dérivées peuvent être n'importe où où l'on travaille avec des fichiers (fichiers, cartographie, canaux, internet). Chacune des classes dérivées définit ces méthodes virtuelles différemment, mais toutes les classes ont la même interface que la classe de base.

 
hoz:

Le manuel donne cet exemple en relation avec le polymorphisme :

Il y a une chose que je ne comprends pas. Si nous utilisons des objets de fonction enfant pour les appels, c'est-à-dire les méthodes dérivées CCircle et CSquare, alors la zone GetArea() peut être calculée en contournant les déclarations de la classe de base. C'est-à-dire ne pas créer du tout de fonctions virtuelles dans une classe de base, et dans les méthodes dérivées créer une simple méthode et c'est tout ! Alors pourquoi avons-nous besoin d'une fonction virtuelle ?

Il est intéressant de voir un exemple adéquat et logique, où l'on peut constater que les fonctions virtuelles apportent un certain avantage. Parce que ce que j'ai vu n'était pas logique, du moins pour moi. Je veux tout de même le comprendre.

Je vais essayer d'esquisser un petit échantillon :

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

Grâce à cette structure, nous n'aurons pas besoin d'entrer dans l'algorithme de travail, qui peut être très grand et complexe (tout est simplifié ici), nous aurons seulement besoin d'ajouter un descendant de plus, m3 dans l'énumération et un cas de plus dans le switch. C'est-à-dire que nous avons unifié les données d'entrée, ce qui évitera les modifications dans la partie principale du programme.

Bien entendu, cela ne sera approprié que si l'algorithme de travail accepte une variété de types en entrée. S'il n'y a qu'un seul type, tout cela est inutile.

 
hoz:

Le manuel donne cet exemple en relation avec le polymorphisme :

Il y a une chose que je ne comprends pas. Si nous utilisons des objets de fonction enfant pour les appels, c'est-à-dire les méthodes dérivées CCircle et CSquare, alors la zone GetArea() peut être calculée en contournant les déclarations de la classe de base. C'est-à-dire ne pas créer du tout de fonctions virtuelles dans une classe de base, et dans les méthodes dérivées créer une simple méthode et c'est tout ! Alors pourquoi avons-nous besoin d'une fonction virtuelle ?

Il est intéressant de voir un exemple adéquat et logique, où l'on peut constater que les fonctions virtuelles apportent un certain avantage. Parce que ce que j'ai vu n'était pas logique, du moins pour moi. J'aimerais tout de même le comprendre.

Voici un exemple simple :

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();
}
Nous utilisons la fonction GetArea() sans savoir pour quelle forme elle est appelée.
 

J'ai ce setter dans une classe :

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

Le compilateur combat généralement cette affectation d'éléments au tableau ColorBySend comme ceci :

'fc_ColorSendBuy' - constant expression required        TradingFunc.mqh 91      23
'fc_ColorSendSell' - constant expression required       TradingFunc.mqh 91      40
'{' - expression expected       TradingFunc.mqh 91      22
Qu'est-ce que ça a à voir ? Est-il vraiment nécessaire d'attribuer des valeurs élément par élément ? N'est-il pas possible de le faire sous forme de liste ? A quoi est-il lié ? Après tout, c'est comme ça que le devoir fonctionne, même dans le manuel...
 
hoz:

J'ai ce setter dans une classe :

Le compilateur jure par cette affectation d'éléments au tableau ColorBySend en général comme ceci :

Qu'est-ce que ça a à voir ? Est-il vraiment nécessaire d'attribuer des valeurs élément par élément ? N'est-il pas possible de le faire sous forme de liste ? Qu'est-ce que ça a à voir avec ça ? Après tout, c'est comme ça que les devoirs sont faits, même dans le manuel...


Une construction du type {quelque chose, quelque chose d'autre} ne peut être qu'un tableau de constantes. Mais les paramètres passés dans la fonction y sont substitués qui sont en fait des variables locales de la fonction. Le modificateur const dans ce cas n'a aucune signification puisqu'il indique seulement que la valeur passée ne peut pas être modifiée. En conséquence,
{fc_ColorSendBuy, fc_ColorSendSell}
est une expression variable que le compilateur ne peut pas comprendre. Hélas.
 
L'initialisation par liste n'est possible qu'avec une déclaration.
Raison: