Domande su OOP (programmazione orientata agli oggetti) - pagina 10

 

i nomi dei parametri non sono importanti... i nomi diversi hanno senso affinché qualcosa non sia confuso con qualcosa...

Potete scrivere alcuni valori nella dichiarazione della funzione,

public:
   void              SetName(string n);

e altri valori nella funzione stessa

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

o si possono nominare i parametri in modo identico ovunque, a seconda di ciò che è più conveniente per chi scrive il codice

 

Nello stesso libro di testo, il codice è venuto fuori:

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

Strana cosa il costruttore:

public:              //--- конструктор класса доступен всем
                     CBaseClass(){m_member=5;return;};
Perché c'è un operatore di ritorno qui?

È la prima volta che vedo questo operatore usato nel costruttore. Infatti, il costruttore viene chiamato automaticamente. E ci sarà comunque un'uscita. Questo operatore ha senso nel costruttore?

 
hoz:

Nello stesso libro di testo, il codice è venuto fuori:

Strana cosa il costruttore:

Perché c'è un operatore di ritorno qui?

È la prima volta che vedo questo operatore usato nel costruttore. Infatti, il costruttore viene chiamato automaticamente. E ci sarà comunque un'uscita. Questo operatore ha senso nel costruttore?

Non è necessario in questo esempio, ma ci può essere un'inizializzazione complessa quando è necessaria un'uscita anticipata.

Il costruttore e il distruttore sono funzioni normali. Solo il costruttore e il distruttore di default sono chiamati automaticamente. Gli altri sono chiamati dall'utente.

 

Il libro di testo dà questo esempio in relazione al polimorfismo:

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

C'è una cosa che non capisco. Se usiamo gli oggetti funzione figli per le chiamate, cioè i metodi derivati CCircle e CSquare, allora l'area GetArea() può essere calcolata bypassando le dichiarazioni nella classe base. Cioè non creare affatto funzioni virtuali nella classe base, e nei metodi derivati creare un semplice metodo e basta! Allora perché abbiamo bisogno di una funzione virtuale?

È interessante vedere un esempio adeguato e logico, dove si può vedere che le funzioni virtuali forniscono qualche beneficio. Perché quello che ho visto non era logico, almeno per me. Vorrei capire lo stesso.

 
hoz:

Il libro di testo dà questo esempio in relazione al polimorfismo:

C'è una cosa che non capisco. Se usiamo gli oggetti funzione figli per le chiamate, cioè i metodi derivati CCircle e CSquare, allora l'area GetArea() può essere calcolata bypassando le dichiarazioni nella classe base. Cioè non creare affatto funzioni virtuali in una classe base, e nei metodi derivati creare un semplice metodo e basta! Allora perché abbiamo bisogno di una funzione virtuale?

È interessante vedere un esempio adeguato e logico, dove si può vedere che le funzioni virtuali forniscono qualche beneficio. Perché quello che ho visto non era logico, almeno per me. Vorrei capire lo stesso.

Questo è il campione più semplice per capire il polimorfismo. Per ottenerlo rapidamente.

Ci sono casi complicati. Lo applicherete quando ne avrete bisogno. Non ha senso preoccuparsi ora. Quando il compito è terminato, ci si deve pensare.

Per esempio, ho una classe base con tutte le possibili interfacce di lettura/scrittura. Ha anche metodi virtuali privati (2 in totale - lettura/scrittura), che collegano questa interfaccia nella classe base con le classi derivate. In realtà le classi derivate possono essere qualsiasi dove c'è lavoro con i file (file, mappatura, canali, internet). Ogni classe derivata definisce questi metodi virtuali in modo diverso, ma tutte le classi hanno la stessa interfaccia della classe base.

 
hoz:

Il libro di testo dà questo esempio in relazione al polimorfismo:

C'è una cosa che non capisco. Se usiamo gli oggetti funzione figli per le chiamate, cioè i metodi derivati CCircle e CSquare, allora l'area GetArea() può essere calcolata bypassando le dichiarazioni nella classe base. Cioè non creare affatto funzioni virtuali nella classe base, e nei metodi derivati creare un semplice metodo e basta! Allora perché abbiamo bisogno di una funzione virtuale?

È interessante vedere un esempio adeguato e logico, dove si può vedere che le funzioni virtuali forniscono qualche beneficio. Perché quello che ho visto non era logico, almeno per me. Voglio capirlo lo stesso.

Cercherò di abbozzare un piccolo campione:

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

Grazie a questa struttura, non avremo bisogno di entrare nell'algoritmo di lavoro, che può essere molto grande e complesso (tutto è semplificato qui), avremo solo bisogno di aggiungere un altro discendente, m3 in enumerazione e un altro caso in switch. Cioè, abbiamo unificato i dati di input, il che eviterà la modifica nella parte principale del programma.

Naturalmente, questo sarà appropriato solo se l'algoritmo di lavoro accetta una varietà di tipi come input. Se c'è solo un tipo, tutto questo è inutile.

 
hoz:

Il libro di testo dà questo esempio in relazione al polimorfismo:

C'è una cosa che non capisco. Se usiamo gli oggetti funzione figli per le chiamate, cioè i metodi derivati CCircle e CSquare, allora l'area GetArea() può essere calcolata bypassando le dichiarazioni nella classe base. Cioè non creare affatto funzioni virtuali in una classe base, e nei metodi derivati creare un semplice metodo e basta! Allora perché abbiamo bisogno di una funzione virtuale?

È interessante vedere un esempio adeguato e logico, dove si può vedere che le funzioni virtuali forniscono qualche beneficio. Perché quello che ho visto non era logico, almeno per me. Vorrei capire lo stesso.

Ecco un semplice esempio:

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();
}
Usiamo la funzione GetArea() senza sapere per quale forma viene chiamata.
 

Ho questo setter in una classe:

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

Il compilatore generalmente combatte questa assegnazione di elementi all'array ColorBySend in questo modo:

'fc_ColorSendBuy' - constant expression required        TradingFunc.mqh 91      23
'fc_ColorSendSell' - constant expression required       TradingFunc.mqh 91      40
'{' - expression expected       TradingFunc.mqh 91      22
Con che cosa ha a che fare questo? È davvero necessario assegnare valori elemento per elemento? Non è possibile farlo come lista? A cosa è legato? Dopo tutto, è così che funziona il compito anche nel libro di testo...
 
hoz:

Ho questo setter in una classe:

Il compilatore giura su questa assegnazione di elementi all'array ColorBySend in generale come questa:

Con che cosa ha a che fare questo? È davvero necessario assegnare valori elemento per elemento? Non è possibile farlo come lista? Cosa c'entra? Dopo tutto, è così che si fa l'assegnazione anche nel libro di testo...


Un costrutto del tipo {qualcosa, qualcos'altro} può essere solo un array di costanti. Ma vengono sostituiti i parametri passati nella funzione che sono in realtà variabili locali della funzione. Il modificatore const in questo caso non ha alcun significato, poiché indica solo che il valore passato non può essere modificato. Come risultato
{fc_ColorSendBuy, fc_ColorSendSell}
è un'espressione di variabile che il compilatore non può capire. Ahimè.
 
L'inizializzazione per lista è possibile solo con una dichiarazione.