Perguntas OOP (Object Oriented Programming) - página 10

 

nomes de parâmetros não são importantes... nomes diferentes fazem sentido para que algo não seja confundido com algo...

Você pode escrever alguns valores na declaração de função,

public:
   void              SetName(string n);

e outros valores na própria função

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

ou você pode nomear os parâmetros de forma idêntica em todos os lugares, o que for mais conveniente para o codificador

 

No mesmo livro didático, o código se deparou:

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

Coisa estranha sobre o construtor:

public:              //--- конструктор класса доступен всем
                     CBaseClass(){m_member=5;return;};
Por que há um operador de retorno aqui?

Esta é a primeira vez que vejo este operador ser utilizado na construtora. Na verdade, o construtor é chamado automaticamente. E haverá uma saída de qualquer maneira. Este operador faz sentido no construtor?

 
hoz:

No mesmo livro didático, o código se deparou:

Coisa estranha sobre o construtor:

Por que existe aqui um operador de retorno?

Esta é a primeira vez que vejo este operador ser utilizado na construtora. Na verdade, o construtor é chamado automaticamente. E haverá uma saída de qualquer maneira. Este operador faz sentido no construtor?

Não é necessário neste exemplo, mas pode haver uma inicialização complexa quando é necessária uma saída antecipada.

O construtor e o destruidor são funções normais. Somente o construtor e o destruidor padrão são chamados automaticamente. Os outros são chamados pelo usuário.

 

O livro didático dá este exemplo em relação ao 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);}//площадь квадрата
  };

Há uma coisa que eu não entendo. Se utilizarmos objetos de função infantil para chamadas, ou seja, métodos derivados CCircle e CSquare, então a área GetArea() pode ser calculada contornando declarações em classe base. Isto é, não criar funções virtuais na classe base e nos métodos derivados criar um método simples e pronto! Então, por que precisamos de uma função virtual?

É interessante ver um exemplo adequado e lógico, onde você pode ver que as funções virtuais proporcionam algum benefício. Porque o que eu vi não era lógico, pelo menos para mim. Eu gostaria de entender tudo da mesma forma.

 
hoz:

O livro didático dá este exemplo em relação ao polimorfismo:

Há uma coisa que eu não entendo. Se usarmos objetos de função infantil para chamadas, ou seja, métodos derivados CCircle e CSquare, então a área GetArea() pode ser calculada contornando declarações em classe base. Isto é, não criar funções virtuais em uma classe base, e em métodos derivados criar um método simples e pronto! Então, por que precisamos de uma função virtual?

É interessante ver um exemplo adequado e lógico, onde você pode ver que as funções virtuais proporcionam algum benefício. Porque o que eu vi não era lógico, pelo menos para mim. Eu gostaria de entender tudo da mesma forma.

Esta é a amostra mais simples para entender o polimorfismo. Para obtê-lo rapidamente.

Há casos complicados. Você a aplicará quando precisar dela. Não faz sentido se preocupar agora. Quando a tarefa estiver concluída, você terá que pensar sobre isso.

Por exemplo, tenho uma classe base com todas as interfaces de leitura/escrita possíveis. Possui também métodos virtuais privados (2 no total - leitura/escrita), que ligam esta interface na classe base com classes derivadas. Na verdade, as classes derivadas podem ser qualquer uma onde haja trabalho com arquivos (arquivos, mapeamento, canais, internet). Cada uma das classes derivadas define estes métodos virtuais de forma diferente, mas todas as classes têm a mesma interface da classe base.

 
hoz:

O livro didático dá este exemplo em relação ao polimorfismo:

Há uma coisa que eu não entendo. Se usarmos objetos de função infantil para chamadas, ou seja, métodos derivados CCircle e CSquare, então a área GetArea() pode ser calculada contornando declarações em classe base. Isto é, não criar funções virtuais na classe base e nos métodos derivados criar um método simples e pronto! Então, por que precisamos de uma função virtual?

É interessante ver um exemplo adequado e lógico, onde você pode ver que as funções virtuais proporcionam algum benefício. Porque o que eu vi não era lógico, pelo menos para mim. Quero entender tudo da mesma forma.

Vou tentar esboçar uma pequena amostra:

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

Graças a esta estrutura, não precisaremos entrar em algoritmo de trabalho, que pode ser muito grande e complexo (tudo é simplificado aqui), precisaremos apenas adicionar mais um descendente, m3 na enumeração e mais um caso no switch. Ou seja, unificamos os dados de entrada, o que evitará a edição na parte principal do programa.

Naturalmente, isto só será apropriado se o algoritmo de trabalho aceitar uma variedade de tipos como entrada. Se existe apenas um tipo, tudo isso é inútil.

 
hoz:

O livro didático dá este exemplo em relação ao polimorfismo:

Há uma coisa que eu não entendo. Se utilizarmos objetos de função infantil para chamadas, ou seja, métodos derivados CCircle e CSquare, então a área GetArea() pode ser calculada contornando declarações em classe base. Isto é, não criar funções virtuais na classe base e nos métodos derivados criar um método simples e pronto! Então, por que precisamos de uma função virtual?

É interessante ver um exemplo adequado e lógico, onde você pode ver que as funções virtuais proporcionam algum benefício. Porque o que eu vi não era lógico, pelo menos para mim. Eu gostaria de entender tudo da mesma forma.

Aqui está um exemplo simples:

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();
}
Usamos a função GetArea() sem saber qual a forma a que ela é chamada.
 

Eu tenho este setter em uma classe:

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

O compilador está geralmente combatendo esta atribuição de elementos para a matriz ColorBySend desta forma:

'fc_ColorSendBuy' - constant expression required        TradingFunc.mqh 91      23
'fc_ColorSendSell' - constant expression required       TradingFunc.mqh 91      40
'{' - expression expected       TradingFunc.mqh 91      22
O que isso tem a ver? É realmente necessário atribuir valores elemento por elemento? Não é possível fazer isso como uma lista? A que está relacionado? Afinal, é assim que a tarefa é feita, mesmo no livro didático.
 
hoz:

Eu tenho este setter em uma classe:

O compilador jura a esta atribuição de elementos para a matriz ColorBySend em geral assim:

O que isso tem a ver? É realmente necessário atribuir valores elemento por elemento? Não é possível fazer isso como uma lista? O que tem a ver com isso? Afinal, é assim que a tarefa é feita, mesmo no livro didático.


Uma construção do tipo {something, something else} só pode ser um conjunto de constantes. Mas os parâmetros passados para a função são ali substituídos que são realmente variáveis locais da função. O modificador const neste caso não tem significado, pois apenas indica que o valor passado não pode ser modificado. Como resultado,
{fc_ColorSendBuy, fc_ColorSendSell}
é uma expressão variável que o compilador não consegue entender. Ai de mim.
 
A inicialização por lista só é possível com uma declaração.