Laburo España: 250.000 ofertas de empleo

Martes, 23 de mayo de 2006

C++ Orientado a objetos

Se explicará:

  1. - La sobrecarga de los operadores menos documentados en la red << y >>
  2. - Polimorfismo
  3. - Herencia virtual
  4. - Llamadas a los métodos y operadores de la clase base desde la derivada.
    Conocimientos previos de:
  1. - Este artículo no explicará los tipos de herencia, ni las posibilidades de acceso a los atributos. Sobre esto ya hay mucho escrito, así que no reinvetaremos la rueda.
  2. - Se requieren conocimientos medios en POO y punteros.


En este artículo explicaré la sobrecarga del operador de extracción >> (sí, este operador se denomina así, aunque pueda no parecerlo) y el operador insertor <<. Más tarde comentaré aspectos de la herencia, polimorfismo y otros asuntos que suelen resultar difíciles de comprender en un primer momento.

1.- OPERADORES:
- Operador insertor <<:
El prototipo de su sobrecarga es:

ostream &operator <<(ostream &flujo, nombre_de_la_clase objeto)
{
flujo << "Mostramos informacion del objeto: "
<< objeto.info << endl;
return flujo;
}

- Operador extractor >>:
El prototipo de este operador sobrecargado es:

istream &operator >>(istream &flujo, nombre_de_la_clase objeto)
{
cout << "Introduzca la informacion del objeto: ";
cin >> objeto.info;
cout << endl;
return flujo;
}

2.- SOBREESCRITURA DE MÉTODOS:
En C++ se pueden sobreescribir los métodos que queramos de tal forma que el compilador llamará al método del objeto correspondiente:

class Uno
{
protected:
int x;
public:
Uno(){x=2;}
mostrar(){cout << "CLASE UNO" << endl;}
};

class Dos : public Uno
{
private:
int y;
public:
Dos(){y=3;}
mostrar(){cout << "CLASE DOS" << endl;}
};

int main()
{
Uno objeto1;
objeto1.mostrar() // Mostrará "CLASE UNO"
Dos objeto2;
objeto2.mostrar() // Mostrará "CLASE DOS"

cin.get();
return 0;
}

En la herencia entre clases en ocasiones nos resultará útil que se ejecuten los métodos de la clase base o padre, y en otras los de la hija o la que hereda.
Como se acaba de ver esto se puede lograr mediante el objeto con el que llamemos al método, pero esto es lo que se conoce como ligadura estática, es decir se debe hacer antes de compilar el programa. Ahora veremos cómo lograrlo en tiempo de ejecución, mediante el polimorfismo (ligadura dinámica).


3.- POLIMORFISMO:
No os asustéis, el polimorfismo, al ir asociado a punteros, suele ser un quebradero de cabeza para los estudiantes de Programación. Lo cierto es que es un concepto sencillo que incorpora una potente flexibilidad al lenguaje C++.

Si en nuestro anterior ejemplo, nuestro main2 fuese:

int main()
{
Dos objeto2;
Uno *puntero = &objeto2;
puntero->mostrar(); // Mostrará "CLASE UNO"

}

Explicación:
Hemos creado un objeto de la clase hija, después un puntero del tipo Uno es decir de la clase base, que hemos apuntado a un objeto del tipo Dos (es decir de la hija). ¿Pero en punteros no se podía apuntar solamente al mismo tipo? Sí, vale, pero en la herencia, podemos usar un puntero a la clase padre y dirigirlo a la hijas.
Lo último es llamar al método mostrar(). Sin embargo no se llama al método de la clase hija, sino a la padre, a pesar de donde apunta el puntero.

¿Cómo lo solucionamos?
Si lo que nos interesa, y esto será así la mayoría de las veces, es llamar al método de la clase hija. definiremos la función mostrar en la clase padre como virtual. Quedaría así:

class Uno
{
protected:
int x;
public:
Uno(){x=2;}
virtual mostrar(){cout << "CLASE UNO" << endl;}
};

Ahora el mismo puntero, de antes, el mismo main de antes, ejecutará el método de la hija, es decir mostrará "CLASE DOS". Esto es en esencia el polimorfismo. Luego "virtual = métodos de la derivada".

4- HERENCIA VIRTUAL:
Pero vamos a complicarlo un poco más, y ya de paso explico la herencia virtual, también bastante desatendida en Internet.

Añadiremos una clase tres y una cuatro, con el siguiente código:

class Tres : public Uno
{
private:
int z;
public:
void mostrar(){cout << "CLASE TRES" << endl;}
};

class Cuatro : virtual public Dos, Tres
{
private:
int w;
public:
void mostrar(){cout << "CLASE CUATRO" << endl;}
};

La clase Tres, es similar a la Dos, hereda de Uno, digamos que Dos y Tres serían "hermanas". La clase Cuatro hereda de ambas, el tema es que de este modo contendría dos clases Uno, luego el compilador dará probablemente error de ambigüedad. ¿Solución?
Como vemos he añadido virtual al tipo de herencia. ¿Qué significa? El asunto es fácil, si hay una clase base repetida en las hijas, solamente se heredará una vez. fácil, ¿no?

- Virtual se hereda:
Ahora bien, volviendo a lo del polimorfismo, el main2, seguiría mostrando "CLASE DOS" si lo ejecutaramos ahora. Pero si hiciesemos:


int main()
{
Cuatro objeto4;
Uno *puntero = &objeto4;
puntero->mostrar(); // Mostrará "CLASE CUATRO"

}

Parece lógico, pero debemos observar que la clase Cuatro no hereda directamente de Uno en ningún momento, y sin embargo el método mostrar sigue siendo virtual. Esto es debido a que virtual se hereda, ¿a qué hora se ve todo más claro? Un último esfuerzo.

5.- LLAMADAS A MÉTODOS DE LA CLASE PADRE:
Me gustaría que recordásemos el primer ejemplo, teníamos dos clases Uno y Dos, y Dos heredaba de Uno, ni virtuales ni nada, simplemente el método estaba sobreescrito ¿ok? Vamos a hacerle unas modificaciones interesantes:

class Uno
{
protected:
int x;
public:
Uno(){x=2;}
mostrar(){cout << "CLASE UNO" << endl;}
};

class Dos : public Uno
{
private:
int y;
public:
Dos(){y=3;}
mostrar()
{
Uno :: mostrar();
cout << "CLASE DOS" << endl;
}
};

int main()
{
Uno objeto1;
objeto1.mostrar() // Mostrará "CLASE UNO"
Dos objeto2;
objeto2.mostrar()
// Mostrará "CLASE UNO"
"CLASE DOS"

cin.get();
return 0;
}

Vale este ejemplo es muy sencillito, lo sé. De este modo clasebase::metodo() podemos llamar a los métodos, a los que tengamos acceso, de la clase base. Igualmente a los operadores clasebase::operator =(parametro);. Así no tendremos que reescribir o copiar y pegar el codigo para añadir algo nuevo. El típico ejemplo de libro de programación:

class Persona
{
protected:
char nombre[30];
public:
void mostrar(){cout << "El nombre de la persona es: "
<< nombre << endl;}
};

class Estudiante : public Persona
{
private:
int curso;
public:
void mostrar()
{
cout << "El nombre de la persona es: " <<
nombre << endl;
cout << "Esta en el curso: " << curso << endl;
}
};

Como vemos si usasemos las llamadas a la padre el código sería:

class Estudiante : public Persona
{
private:
int curso;
public:
void mostrar()
{
Persona::mostrar();
cout << "Esta en el curso: "
<< curso << endl;
}
};

En este caso, la diferencia apreciable es mínima, pero en código extensos simplifica depuración y estructuración, y ahorra errores y tiempo de "reinventar la rueda".

Si interesa, otro día podría comentar el operador [] (Para construir arrays seguros), operadores como new y delete, clases abstractas, y algún que otro truquillo, espero comentarios..

Un saludo

Por: Miguel Araujo | Programación | Comentarios (3) | Referencias (0)

Comentarios

Muy bueno. Agrego una nota histórica: los operadores << y >> en realidad no tienen ninguna significación particular en C++, ni se relacionan de forma 'implícita' con streams, sino que son una forma interesante de sobrecargar operadores ya existentes. Eso es parte de lo 'bello' de C++ (la economía de elementos intrínsecos).

Por otro lado, si se puede sugerir un tema, sería muy interesante "cómo crear streams derivados o filtros" para por ejemplo comprimir, cifrar, tokenizar contenido.

Saludos y felicitaciones por el material.

Ignacio | 23-05-2006 14:42:11

Gracias por tus felicitaciones Ignacio. El tema que propones resultaría muy interesante, sin embargo, creo que se escapa a mis conocimientos por el momento. Lo tendré en cuenta de todas formas.

Gracias por el apoyo.
Un saludo

Miguel | 24-05-2006 20:20:23

los felicito la información que brindan esta muy padre ademas la complementan con ejemplos y eso es lo que nos sirve a nosotros los estudiantes...Sigan adelante.....

Nelly | 25-01-2008 17:29:49

Comentar


Recordar datos

Búsqueda

Acerca de InfoLinux


Tu punto de encuentro GNU/Linux: Manuales, anécdotas, curiosidades, consejos y trucos, ¡sácale partido a tu ordenador!


Linuxeros Online:

Blogs que leo

Sindicación

Añadir a Feedness
RDF XML ATOM

Créditos

Diseñado por Studio.st
Online gracias a Bitacoras.com