Publi

Polimorfismos: enrevesando la herencia entre clases [C++]

Cuando desarrollamos bajo el paradigma de la programación orientada a objetos, a poco que avanzamos tenemos la sensación de estar escribiendo más para hacer lo mismo que hacíamos antes sin clases ni instancias ni tanta estructuración.
Pero contamos con herramientas que, aunque al principio nos sonarán algo chocantes, a la larga terminarán abriéndonos un mundo de posibilidades y comprenderemos por qué es tan importante, tan utilizado y se exige en multitud de universidades y centros educativos.
Uno de esos grandes avances que nos permite la programación orientada a objetos y que hoy particularizaremos a C++ son los polimorfismos.

Imaginemos (el ejemplo es clases_animalun poco tonto, lo reconozco, pero es lo más sencillo que se me ocurre para no llenar el post de código. Estoy abierto a sugerencias) que vamos a crear un software de mascota virtual, para eso construimos una clase padre (o superclase) llamada Animal. El sistema está en una fase muy temprana de desarrollo y los animales sólo pueden emitir sonidos (aunque en este programa se pondrá una onomatopeya por pantalla).

A partir de la clase Animal crearemos las clases Perro y Gato. Lo que necesitamos es que todos los miembros de Animal tengan un método hablar() que les permita emitir su sonido característico. Nuestra función main() queda así:

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
  Mascota *p, *g;
 
  p=new Perro("Ramiro");
  g=new Gato("Lancelot");

  cout << p->hablar() << endl;
  cout << g->hablar() << endl;

  return 0;
}

Lo que deseamos es que p (declarado como Mascota, pero en realidad es de clase Perro), diga «GUAU» y g (instancia de Gato) diga «MIAU». Para eso necesitamos crear un polimorfismo (será un método que en cada subclase (clase heredera, o clase hija) ejecute el método correspondiente dependiendo de la clase de la instancia que tengamos. En C++ lo hacemos con métodos virtuales:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <string>

using namespace std;

class Mascota
{
public:
  Mascota(string nombre, int patas);
  virtual string hablar() = 0;

private:
  string nombre;
  int patas;
};

Mascota::Mascota(string nombre, int patas):nombre(nombre),patas(patas)
{
  cout << "Acaba de nacer tu mascota "<<nombre<<" con "<<patas<<"patas"<<endl;
}

class Perro : public  Mascota
{
public:
  Perro(string nombre);

  string hablar();
};

Perro::Perro(string nombre):Mascota(nombre, 4)
{
}

string Perro::hablar()
{
  return "GUAU";
}

class Gato : public  Mascota
{
public:
  Gato(string nombre);

  string hablar();
};

Gato::Gato(string nombre):Mascota(nombre, 4)
{
}

string Gato::hablar()
{
  return "MIAU";
}

Cuando lo ejecutemos, una vez creados el Perro (Ramiro) y el Gato (Lancelot), cada uno dirá su palabra correspondiente. Para entender la diferencia de lo que origina utilizar virtual o no utilizarlo, lo mejor es probarlo. Vemos que para crear la función «virtual string hablar()» escribimos =0 (es una forma especial para que la función quede definida y no sólo declarada). Por tanto, para eliminar el virtual, debemos declarar la función como virtual string hablar() y definir su comportamiento. El fragmento quedaría así:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Mascota
{
public:
  Mascota(string nombre, int patas);
  string hablar();

private:
  string nombre;
  int patas;
};

Mascota::Mascota(string nombre, int patas):nombre(nombre),patas(patas)
{
  cout << "Acaba de nacer tu mascota "<<nombre<<" con "<<patas<<"patas"<<endl;
}

string Mascota::hablar()
{
  return "BUU";
}

En este caso, un «Animal por defecto» (que no pertenecería a ninguna especie) diría «BUU», si ejecutamos el programa tal cual lo tenemos ahora, la salida sería:

Acaba de nacer tu mascota Ramiro con 4patas
Acaba de nacer tu mascota Lancelot con 4patas
BUU
BUU

¡ Los dos dirían BUU ! Y eso es porque inicialmente están declaradas como Animal, así que al llamar al método hablar() se llamará al de la clase Animal en lugar de Perro o Gato.

Ahora hacemos una pequeña modificación. Cambiamos los métodos virtual hablar() por palabra() y los ponemos en la parte privada, para el acceso desde main() creamos otra función hablar() no virtual en Mascota, cuyo contenido será:

1
2
3
4
string Mascota::hablar()
{
  return nombre+" dice: "+this->palabra();
}

El objetivo es que cada animal, antes de decir su palabra escriba: «(minombre) dice: (mipalabra)», en el caso del perro: «Ramiro dice: GUAU». Aunque claro, es un método de Animal, por tanto this apuntaría a un Animal, que lo es, pero también es un Perro y como perro tiene su palabra característica y su función palabra() definida y propia, en la parte privada, por lo que intuitivamente no tendríamos acceso; pero virtual también funciona aquí para poder seleccionar el método hablar() de la clase correspondiente.

El resultado final queda así (por si quieres copiar y pegar):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <iostream>
#include <string>

using namespace std;

class Mascota
{
public:
  Mascota(string nombre, int patas);
  string hablar();

private:
  virtual string palabra();

  string nombre;
  int patas;
};

Mascota::Mascota(string nombre, int patas):nombre(nombre),patas(patas)
{
  cout << "Acaba de nacer tu mascota "<<nombre<<" con "<<patas<<"patas"<<endl;
}

string Mascota::palabra()
{
  return "BUUU";
}

string Mascota::hablar()
{
  return nombre+" dice: "+this->palabra();
}

class Perro : public  Mascota
{
public:
  Perro(string nombre);

  string palabra();
};

Perro::Perro(string nombre):Mascota(nombre, 4)
{
}

string Perro::palabra()
{
  return "GUAU";
}

class Gato : public  Mascota
{
public:
  Gato(string nombre);

  string palabra();
};

Gato::Gato(string nombre):Mascota(nombre, 4)
{
}

string Gato::palabra()
{
  return "MIAU";
}

int main()
{
  Mascota *m, *g;
 
  m=new Perro("Ramiro");
  g=new Gato("Lancelot");

  cout << m->hablar() << endl;
  cout << g->hablar() << endl;
  return 0;
}

Nota: A veces también se llama polimorfismo a la sobrecarga (de métodos, por ejemplo, donde podemos describir varios métodos homónimos (todos llevarán el mismo nombre), pero con diferentes tipos de argumento, y en función de ellos, el compilador podrá saber a qué método llamamos. Todo esto tiene como función hacer la programación algo más intuitiva y natural.

También podría interesarte....

There are 5 comments left Ir a comentario

  1. Pingback: Bitacoras.com /

  2. Pingback: Poesía binaria » Destructores virtuales en C++ /

  3. Pingback: Poesía binaria » ¿ Y tú qué eres ? Identificando clases heredadas en C++ /

  4. Daniel Leal /
    Usando Google Chrome Google Chrome 37.0.2062.122 en Windows Windows 8

    Aca hay un ejemplo de como portar el ejemplo a Arduino.
    http://forum.arduino.cc/index.php?topic=270954.0

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 32.0 en Linux Linux

      Gracias por el aporte Daniel !

Leave a Reply to Gaspar Fernández Cancle Reply