Preguntas de POO (Programación Orientada a Objetos) - página 10

 

los nombres de los parámetros no son importantes... los diferentes nombres tienen sentido para que no se confunda algo con algo...

Puede escribir algunos valores en la declaración de la función,

public:
   void              SetName(string n);

y otros valores en la propia función

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

o puede nombrar los parámetros de forma idéntica en todas partes, lo que sea más conveniente para el escritor de código

 

En el mismo libro de texto, el código apareció:

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

Es extraño lo del constructor:

public:              //--- конструктор класса доступен всем
                     CBaseClass(){m_member=5;return;};
¿Por qué hay un operador de retorno aquí?

Es la primera vez que veo que se utiliza este operador en el constructor. De hecho, el constructor se llama automáticamente. Y habrá una salida de todos modos. ¿Tiene sentido este operador en el constructor?

 
hoz:

En el mismo libro de texto, el código apareció:

Es extraño lo del constructor:

¿Por qué hay un operador de retorno aquí?

Es la primera vez que veo que se utiliza este operador en el constructor. De hecho, el constructor se llama automáticamente. Y habrá una salida de todos modos. ¿Tiene sentido este operador en el constructor?

No es necesario en este ejemplo, pero puede haber una inicialización compleja cuando se necesita una salida temprana.

El constructor y el destructor son funciones normales. Sólo el constructor y el destructor por defecto son llamados automáticamente. Los demás son llamados por el usuario.

 

El libro de texto da este ejemplo en relación con el 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);}//площадь квадрата
  };

Hay una cosa que no entiendo. Si utilizamos objetos de función hijos para las llamadas, es decir, los métodos derivados CCircle y CSquare, entonces el área de GetArea() puede ser calculada saltándose las declaraciones en la clase base. Es decir, no crear funciones virtuales en la clase base en absoluto, y en los métodos derivados crear un método simple y ya está. Entonces, ¿por qué necesitamos una función virtual?

Es interesante ver un ejemplo adecuado y lógico, en el que se puede ver que las funciones virtuales aportan algún beneficio. Porque lo que vi no era lógico, al menos para mí. Me gustaría entenderlo igualmente.

 
hoz:

El libro de texto da este ejemplo en relación con el polimorfismo:

Hay una cosa que no entiendo. Si utilizamos objetos de función hijos para las llamadas, es decir, los métodos derivados CCircle y CSquare, entonces el área de GetArea() puede ser calculada saltándose las declaraciones en la clase base. Es decir, no crear funciones virtuales en una clase base en absoluto, y en los métodos derivados crear un método simple y ya está. Entonces, ¿por qué necesitamos una función virtual?

Es interesante ver un ejemplo adecuado y lógico, en el que se ve que las funciones virtuales aportan algún beneficio. Porque lo que vi no era lógico, al menos para mí. Me gustaría entenderlo igualmente.

Esta es la muestra más sencilla para entender el polimorfismo. Para conseguirlo rápidamente.

Hay casos complicados. Lo aplicará cuando lo necesite. No tiene sentido molestarse ahora. Cuando la tarea esté hecha, tendrás que pensar en ella.

Por ejemplo, tengo una clase base con todas las interfaces de lectura/escritura posibles. También tiene métodos virtuales privados (2 en total - lectura/escritura), que vinculan esta interfaz en la clase base con las clases derivadas. En realidad las clases derivadas pueden ser cualquiera donde haya trabajo con archivos (archivos, mapeo, canales, internet). Cada una de las clases derivadas define estos métodos virtuales de forma diferente, pero todas las clases tienen la misma interfaz de la clase base.

 
hoz:

El libro de texto da este ejemplo en relación con el polimorfismo:

Hay una cosa que no entiendo. Si utilizamos los objetos de la función hija para las llamadas, es decir, los métodos derivados CCircle y CSquare, entonces el área de GetArea() se puede calcular omitiendo las declaraciones en la clase base. Es decir, no crear funciones virtuales en la clase base en absoluto, y en los métodos derivados crear un método simple y ya está. Entonces, ¿por qué necesitamos una función virtual?

Es interesante ver un ejemplo adecuado y lógico, en el que se ve que las funciones virtuales aportan algún beneficio. Porque lo que vi no era lógico, al menos para mí. Quiero entenderlo igualmente.

Intentaré esbozar una pequeña muestra:

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

Gracias a esta estructura, no necesitaremos entrar en el algoritmo de trabajo, que puede ser muy grande y complejo (todo está simplificado aquí), sólo tendremos que añadir un descendiente más, m3 en la enumeración y un caso más en el interruptor. Es decir, hemos unificado los datos de entrada, lo que evitará la edición en la parte principal del programa.

Por supuesto, esto sólo será apropiado si el algoritmo de trabajo acepta una variedad de tipos como entrada. Si sólo hay un tipo, todo esto es inútil.

 
hoz:

El libro de texto da este ejemplo en relación con el polimorfismo:

Hay una cosa que no entiendo. Si utilizamos objetos de función hijos para las llamadas, es decir, los métodos derivados CCircle y CSquare, entonces el área de GetArea() puede ser calculada saltándose las declaraciones en la clase base. Es decir, no crear funciones virtuales en una clase base en absoluto, y en los métodos derivados crear un método simple y ya está. Entonces, ¿por qué necesitamos una función virtual?

Es interesante ver un ejemplo adecuado y lógico, en el que se puede ver que las funciones virtuales aportan algún beneficio. Porque lo que vi no era lógico, al menos para mí. Me gustaría entenderlo igualmente.

He aquí un ejemplo sencillo:

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();
}
Utilizamos la función GetArea() sin saber a qué forma se llama.
 

Tengo este colocador en una clase:

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

El compilador generalmente está luchando contra esta asignación de elementos a la matriz ColorBySend así:

'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é tiene esto que ver? ¿Es realmente necesario asignar valores elemento por elemento? ¿No es posible hacerlo como una lista? ¿Con qué está relacionado? Después de todo, así es como funciona la tarea incluso en el libro de texto...
 
hoz:

Tengo este colocador en una clase:

El compilador jura esta asignación de elementos al array ColorBySend en general así:

¿Qué tiene esto que ver? ¿Es realmente necesario asignar valores elemento por elemento? ¿No es posible hacerlo como una lista? ¿Qué tiene que ver? Después de todo, así es como se hace la asignación incluso en el libro de texto...


Una construcción del tipo {algo, algo más} sólo puede ser una matriz de constantes. Pero allí se sustituyen los parámetros pasados a la función que en realidad son variables locales de la función. El modificador const en este caso no tiene ningún significado ya que sólo indica que el valor pasado no puede ser modificado. Como resultado
{fc_ColorSendBuy, fc_ColorSendSell}
es una expresión variable que el compilador no puede entender. Ay.
 
La inicialización por lista sólo es posible con una declaración.