Polimorfismo

Polimorfismo é uma oportunidade para diferentes classes de objetos, relacionadas através de herança, de responder de várias formas quando o mesmo elemento de função for chamado. Isso ajuda a criar um mecanismo universal descrevendo o comportamento não apenas da classe base, mas também das classes descendentes.

Vamos continuar a desenvolver uma classe base CShape, e definir uma função membro GetArea(), destinado a calcular a área de uma forma. Em todas as classes descendentes, produzidas por herança a partir da classe base, nós redefinimos esta função de acordo com as regras de cálculo de área de uma forma (shape) particular.

Para um quadrado (classe CSquare), a área é calculada através de seu lado, para um círculo (classe CCircle), a área é expressa através de seu raio, etc. Nós podemos criar um array para armazenas objetos do tipo CShape, no qual tanto objetos da classe base como todos os objetos de classes descendentes podem ser armazenados. Mais adiante, podemos chamar a mesma função para cada elemento do array.

Exemplo:

//--- Classe Base
class CShape
  {
protected
   int            m_type;                // tipo da forma
   int            m_xpos;                // X - coordenada do ponto base
   int            m_ypos;                // Y - coordenada do ponto de base
public:
   void           CShape(){m_type=0;};   // construtor, tipo=0
   int            GetType(){return(m_type);};// retorna o tipo da forma
virtual
   double         GetArea(){return (0); }// retorna a área da forma
  };

Agora, todas as classes derivadas têm uma função membro getArea(), que retorna o valor zero. A implementação desta função em cada descendente não será a mesma.

//--- A classe derivada Circle
class CCircle : public CShape            // Depois do dois pontos definimos a classe base
  {                                      // a partir do qual a herança é feita
private:
   double         m_radius;              // raio do círculo
 
public:
   void           CCircle(){m_type=1;};  // construtor, tipo=1 
   void           SetRadius(double r){m_radius=r;};
   virtual double GetArea(){return (3.14*m_radius*m_radius);}// área do círculo
  };

Para a classe Square, a declaração é a mesma:

//--- A classe derivada Square
class CSquare : public CShape            // Depois do dois pontos definimos a classe base
  {                                      // a partir do qual a herança é feita
private:
   double          m_square_side;        // lado do quadrado
 
public:
   void            CSquare(){m_type=2;}; // construtor, tipo=2
   void            SetSide(double s){m_square_side=s;};
   virtual double  GetArea(){return (m_square_side*m_square_side);}// área quadrada
  };

Para calcular a área do quadrado e círculo, precisamos dos correspondentes valores de m_radius e m_square_side, por isso nós adicionamos as funções SetRadius() e SetSide() na declaração da correspondente classe.

Assumimos que objetos de diferentes tipos (CCircle e CSquare) derivados do tipo base CShape são usados em nosso programa. Polimorfismo permite criar um array de objetos da classe base CShape, mas ao declarar este array, estes objetos são desconhecidos e o tipo deles é indefinido.

A decisão sobre que tipo de objeto estará contido em cada elemento do array será tomada diretamente durante a execução do programa. Isso envolve a criação dinâmica de objetos das classes apropriadas, e portanto a necessidade do uso de ponteiros de objeto ao invés de objetos.

O operador new é usado para criação dinâmica de objetos. Cada um destes objetos devem ser individualmente e explicitamente excluídos usando o operador delete. Portanto declararemos um array de ponteiros do tipo CShape, e criaremos um objeto de um tipo apropriado para cada elemento (new Class_Name), como mostrado no exemplo de script seguinte:

//+------------------------------------------------------------------+
//| Programa Script da função start (iniciar)                        |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Declararmos um array de ponteiros de objeto do tipo base
   CShape *shapes[5];   // Um array de ponteiros para objetos CShape
 
//--- Aqui preenchemos o array com objetos derivados
//--- Declaramos um ponteiro para o objeto de tipo CCircle
   CCircle *circle=new CCircle();
//--- Definimos propriedades do objeto usando o ponteiro do círculo
   circle.SetRadius(2.5);
//--- Colocamos o valor do ponteiro em shapes[0]
   shapes[0]=circle;
 
//--- Criamos um outro objeto CCircle e escrevemos seu ponteiro em shapes[1]
   circle=new CCircle();
   shapes[1]=circle;
   circle.SetRadius(5);
 
//--- Aqui nós intencionalmente "esquecemos" de definir um valor para shapes[2]
//circle=new CCircle();
//circle.SetRadius(10);
//shapes[2]=circle;
 
//--- Definimos NULL para o elemento que não é usado
   shapes[2]=NULL;
 
//--- Criamos um objeto CSquare e escrevemos seu ponteiro em shapes[3]
   CSquare *square=new CSquare();
   square.SetSide(5);
   shapes[3]=square;
 
//--- Criamos um objeto CSquare e escrevemos seu ponteiro em shapes[4]
   square=new CSquare();
   square.SetSide(10);
   shapes[4]=square;
 
//--- Temos um array de ponteiros, obtemos seu tamanho
   int total=ArraySize(shapes);
//--- Passamos em um loop através de todos os ponteiros no array
   for(int i=0; i<5;i++)
     {
      //--- Se o ponteiro no índice especificado é válido
      if(CheckPointer(shapes[i])!=POINTER_INVALID)
        {
         //--- Imprimi o tipo e área da forma
         PrintFormat("O objeto do tipo %d tem a área %G",
               shapes[i].GetType(),
               shapes[i].GetArea());
        }
      //--- Se o ponteiro tem o tipo POINTER_INVALID
      else
        {
         //--- Notificamos um erro
         PrintFormat("Objeto shapes[%d] não foi inicializado! Seu ponteiro pe %s",
                     i,EnumToString(CheckPointer(shapes[i])));
        }
     }
 
//--- Devemos excluir todos os objetos criados dinamicamente
   for(int i=0;i<total;i++)
     {
      //--- Nós podemos excluir somente objetos com ponteiros do tipo POINTER_DYNAMIC
      if(CheckPointer(shapes[i])==POINTER_DYNAMIC)
        {
         //--- Notificação de exclusão
         PrintFormat("Excluindo shapes[%d]",i);
         //--- Excluímos um objeto por meio de seu ponteiro
         delete shapes[i];
        }
     }
  }

Favor notar que ao excluir um objeto usando o operador delete, o tipo de seu ponteiro deve ser verificado. Somente objetos com ponteiro do tipo POINTER_DYNAMIC podem ser excluídos usando delete. Para ponteiros de outros tipos, um erro será retornado.

Além da redefinição de funções durante herança, o polimorfismo também inclui a implementação de uma mesma função com diferentes conjuntos de parâmetros dentro de uma classe. Isso significa que a classe pode ter várias funções com o mesmo nome, mas com um tipo e/ou conjunto de parâmetros diferentes. Neste caso, o polimorfismo é implementado através de sobrecarga de função.

Também Veja

Standard Library