Anuncios

Bienvenidos sean a este post, hoy revisitaremos el concepto de virtual.

Anuncios

En este post vimos como es el concepto de virtual pero tambien existe otra forma de virtual de la cual no hablamos y le da nombre a este post. Pero antes de desarrollar este tema veamos el siguiente ejemplo:

#include <iostream>

class Animal
{
public:
        Animal(int edad): suEdad(edad) {
                std::cout << "Constructor de Animal\n";
        }
        virtual ~Animal() { std::cout << "Destructor de Animal\n"; }
        int GetEdad() const { return suEdad; }
        virtual void Hablar() const { std::cout << "Animal hablar\n"; }
        virtual void Mover() const { std::cout << "Animal mover\n"; }
        virtual void Reproducir() const { std::cout << "Animal reproducir\n"; }
private:
        int suEdad;
};

class Mamifero : public Animal
{
public:
        Mamifero(int edad): Animal(edad) {
                std::cout << "Constructor de Mamifero\n";
        }
        virtual ~Mamifero() { std::cout << "Destructor de Mamifero\n"; }
        void Reproducir() const {
                std::cout << "Mamifero reproduciendose\n";
        }
};

class Ave : public Animal
{
        Ave(int edad): Animal(edad) {
                std::cout << "Constructor de Ave\n";
        }
        virtual ~Ave() { std::cout << "Destructor de Ave\n"; }
        void Hablar() const { std::cout << "Groak "; }
        void Mover() const { std::cout << "Yo vuelo para moverme"; }
        void Reproducir() const { std::cout << "Pongo huevos\n"; }
};

class Perro : public Mamifero
{
public:
        Perro(int edad): Mamifero(edad) {
                std::cout << "Constructor de Perro\n";
        }
        virtual ~Perro() { std::cout << "Destructor de Perro\n"; }
        void Hablar() const { std::cout << "Guau! "; }
        void Mover() const { std::cout << "Perro moviendose\n"; }
};

int main()
{
        Animal* pAn = new Animal(7);
        std::cout << "Animal edad: " <<pAn->GetEdad() << std::endl;
        pAn->Hablar();
        delete pAn;
        Perro* pPe = new Perro(10);
        std::cout << "Perro edad: " << pPe->GetEdad() << std::endl;
        pPe->Hablar();
        pPe->Reproducir();
        delete pPe;

        return 0;
}
Anuncios
Anuncios

Primero vamos a definir una clase que sera nuestra base. Esta sera Animal, en ella tendremos varios metodos de manera publica y una propiedad en privada. La propiedad almacenara el valor que representara su edad. Volviendo a la parte publica tenemos el constructor donde le pasamos un argumento para iniciar el valor de suEdad e indicaremos que fue llamado. El destructor solo mostrara un mensaje indicando que fue invocado. Despues tenemos un metodo que se encarga unicamente de devolver el valor de suEdad y luego los metodos virtuales para ser pasados a las herederas. Estas seran virtuales y sera para tres acciones: Hablar, Mover y Reproducir. Donde en cada uno indicaremos la accion que realiza y desde cual clase.

Anuncios

La siguiente clase es Mamifero y sera heredera de Animal. En esta volvamos a implementar un constructor y un destructor similar a los anteriores. En cada uno mostraremos un mensaje indicando cual es cuando sean invocados. En el caso del constructor recibira un valor que usaremos para establecer la edad del mismo y para ello llamamos al constructor de Animal. Y en esta clase solo redefiniremos a Reproducir con un mensaje indicando la reproduccion del mamifero.

Anuncios

La siguiente clase es Ave y tambien es heredera de Animal. Nuevamente tenemos un constructor y desttuctor similares a los anteriores. Siempre indicando quienes son cuando sean invocados, y el constructor tambien llama al constructor de Animal. En esta nueva clase redefiniremos a los tres metodos. Para que sean mas acordes al animal de esta clase.

Anuncios

La ultima clase sera Perro y esta sera heredera de Mamifero. Nuevamente tenemos un constructor y un destructor donde repetiremos lo mismo que vimos anteriormente pero en el constructor ahora para establecer el valor de suEdad usamos el constructor de Mamifero. En este redefiniremos a dos metodos de los heredados de Animal. Estos son Mover y Hablar para que sean mas acordes con el tipo de animal que representa la clase.

Anuncios

Ya tenemos las clases creadas y definidas, pasemos a hablar sobre main. Primero crearemos un puntero de tipo Animal y le asignaremos un objeto de este tipo. De este llamaremos a GetEdad para obtener la edad asignada. Para luego usar al metodo Hablar y borramos el puntero. Creamos un nuevo puntero pero del tipo Perro y le asignaremos un objeto. Nuevamente llamamos a GetEdad para obtener el valor asignado. Luego llamamos a Hablar y Reproducir para mostrar sus respectivos mensajes. Para finalmente eliminarr el puntero. Compilemos y veamos como es la salida:

$ ./pura
Constructor de Animal
Animal edad: 7
Animal hablar
Destructor de Animal
Constructor de Animal
Constructor de Mamifero
Constructor de Perro
Perro edad: 10
Guau! Mamifero reproduciendose
Destructor de Perro
Destructor de Mamifero
Destructor de Animal
$
Anuncios

Como podemos ver se devolvio todo lo que creamos y solicitamos. Pero tenemos un inconveniente, esta es la posibilidad de crear objetos desde la clase base, Animal, y eso no se deberia poder hacer. Aqui es donde entran en accion las virtuales puras. Para ello deben modificar la clase Animal de la siguiente manera:

class Animal
{
public:
        Animal(int edad): suEdad(edad) {
                std::cout << "Constructor de Animal\n";
        }
        virtual ~Animal() { std::cout << "Destructor de Animal\n"; }
        int GetEdad() const { return suEdad; }
        virtual void Hablar() const = 0;
        virtual void Mover() const = 0;
        virtual void Reproducir() const = 0;
private:
        int suEdad;
};
Anuncios

Simplemente establecemos a las tres virtuales con el igual a cero al final de cada una. El resto no necesita ninguna modificacion. Si vuelven a compilarlo, nos devolvera el siguiente mensaje:

$ g++ pura.cpp -o pura
pura.cpp: In function ‘int main()’:
pura.cpp:54:35: error: invalid new-expression of abstract class type ‘Animal’
   54 |         Animal* pAn = new Animal(7);
      |                                   ^
pura.cpp:3:7: note:   because the following virtual functions are pure within ‘Animal’:
    3 | class Animal
      |       ^~~~~~
pura.cpp:11:22: note:     ‘virtual void Animal::Hablar() const’
   11 |         virtual void Hablar() const = 0;
      |                      ^~~~~~
pura.cpp:12:22: note:     ‘virtual void Animal::Mover() const’
   12 |         virtual void Mover() const = 0;
      |                      ^~~~~
pura.cpp:13:22: note:     ‘virtual void Animal::Reproducir() const’
   13 |         virtual void Reproducir() const = 0;
      |                      ^~~~~~~~~~
$
Anuncios

Como la clase paso a ser abstracta ya no se permite crear objetos o instancias desde esta. Y luego nos indica porque establecimos a los metodos como virtuales puras, la clase paso a ser abstracta. Para que funcione deben modificar el codigo de main de la siguiente manera:

int main()
{
        Perro* pPe = new Perro(10);
        std::cout << "Perro edad: " << pPe->GetEdad() << std::endl;
        pPe->Hablar();
        pPe->Reproducir();
        delete pPe;

        return 0;
}
Anuncios

Simplemente eliminamos todo lo relacionado a la clase Animal y dejamos a Perro. Este es exactamente igual al que teniamos anteriorrmente. Compilemos y veamos como es la salida:

$ ./pura
Constructor de Animal
Constructor de Mamifero
Constructor de Perro
Perro edad: 10
Guau! Mamifero reproduciendose
Destructor de Perro
Destructor de Mamifero
Destructor de Animal
$
Anuncios

Esto significa que el codigo funciona correctamente y ahora no permite crear objetos desde la clase base. La cual permanecera invisible y solo servira como referencia para las herederas de la misma. Busquen en el bloque del main la siguiente linea:

Perro* pPe = new Perro(10);
Anuncios

Y modifiquenla de la siguiente manera:

Animal* pPe = new Perro(10);
Anuncios

Si lo compilan y ejecutan deben obtener la misma salida que anteriormente. Que una clase se convierta en abstracta, no significa que no se pueda usar como tipo. Recuerden que entre herederos siempre podran acceder pero no podran ejecutar un new de este tipo. Por esta razon, el puntero es de tipo Animal pero el objeto donde apunta es de tipo Perro. Esta forma de trabajar la ven en otros lenguajes derivados de C++, como por ejemplo Java, y se los denomina como interface. En este y otros lenguajes en las interfaces solo se declaran metodos y propiedades. Siendo la responsabilidad de la definicion de cada uno de ellos en las clases herederas de estas.

Anuncios

En resumen, hoy hemos visto virtual pura, que es, para que sirve, como se implementa, y hemos visto un ejemplo practico para verla en accion.. Espero les haya resultado de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Donatión

It’s for site maintenance, thanks!

$1.50