Introducción a la Programación Orientada a Objetos

 

Por: Demian C. Panello        demianpanello@yahoo.com.ar                  

Capítulo I

Indice rápido del capítulo 1:                                                              

 

En esta sección abordaremos los conceptos básicos - iniciales, de la Programación Orientada a Objetos, (POO).

Se verán algunos conceptos como clases, polimorfismo, herencia, etc., directamente con ejemplos, sin utilizar muchas definiciones "académicas", tan sólo ejemplos.

 

Clases:

 

En lenguaje C tradicional existen las estructuras de datos, las cuales se definen con la palabra clave struct, ejemplo:

 

struct Coordenadas

{

    int x;

    int y;

    int z;

}

 

Con una estructura uno crea un tipo de dato nuevo, en este caso, se puede declarar una variable de tipo Coordenadas, la cual puede almacenar 3 valores enteros:

 

struct Coordenadas coo;    //Declaración de la variable coo de tipo Coordenadas

coo.x=7;                            //Valores iniciales para los datos miembros.

coo.y=15;

coo.z=55;

 

x, y, z son los "datos miembros" de la estructura. Para manipular estos datos, (asignarles un valor inicial, cargarlos, mostrarlos, etc.), uno puede escribir funciones globales en su programa. Ejemplo:

 

void Carga(void)

void Muestra(void)

 

Bueno, se podría decir que una estructura es el "antepasado" más directo de una clase.

¿Por qué?.

Que tal si las funciones con las cuales uno manipula los datos de la estructura formaran parte de ella, o sea, una estructura tal que además de definir sus datos miembros también definiera las funciones para manipularlos. Este tipo de estructuras existe en C++ y se definen igual que las estructuras de C pero además uno puede declarar las funciones.

Mire el siguiente ejemplo: (para estos ejemplos puede usar Visual C++ o Borland C++ 3.1, con cualquiera de ellos funcionan).

 

//Estructura con funciones miembros.
//Autor: Demian C. Panello.

#include <iostream.h>

struct Coordenadas
{
    int x,y,z;

void Cargar(void)        //Función miembro que carga los datos.
    {
    x=8;
    y=9;
    z=10;
    }
void Mostrar(void)        //Función miembro que muestra el contenido de los datos.
    {
    cout << x <<endl;
    cout << y <<endl;
    cout << z <<endl;
    }
};

void main(void)
{
    struct Coordenadas coo;    //Se define una variable, (coo), de tipo Coordenadas.

    coo.Cargar();                        //Llamadas a las funciones de coo.
    coo.Mostrar();
}

 

Ahora examine el siguiente programa y encuentre las diferencias con el anterior:

 

//Lo mismo pero con una clase.
//Autor: Demian C. Panello.

#include <iostream.h>

class Coordenadas
{
    int x,y,z;


public:
    void Cargar(void)
    {
        x=8;
        y=9;
        z=10;
    }
    void Mostrar(void)
    {
       cout << x <<endl;
       cout << y <<endl;
       cout << z <<endl;
    }

};

void main(void)
{
    Coordenadas coo;

    coo.Cargar();
    coo.Mostrar();

}

 

¿Encontró las diferencias?.

La verdad, no son muchas. En lugar de struct se pone class, luego se agrega la etiqueta public, antes de definir las funciones miembros, ya que para una estructura los datos miembros y funciones miembros son por defecto públicos, pero en una clase por defecto los datos miembros son privados, (esto forma parte, entre otras cosas, de lo que se llama "encapsular"),  y sólo las funciones públicas pueden tener acceso a los datos privados.

Y la otra diferencia es en el momento de definir(*) la variable coo, no hace falta especificar la palabra class así como se hizo con struct.

 

(*) En la POO, utilizando clases, ya no se habla de "definir" una variable de una clase en particular, sino que se crea una "instancia" o un objeto de dicha clase.

 

¿Por qué usar clases y no estructuras?.

 

A veces la diferencia, aparte de la sintaxis, no es del todo "pesada" como para justificar una clase. En este ejemplo no hacía falta definir una clase, la versión de la estructura es más que suficiente. 

Pero cuando el concepto del objeto a crear es un tanto más complejo, y preocupa, por ejemplo, la protección de los contenidos de los datos miembros, o se tiene una gran cantidad de funciones miembros, o simplemente se pretende en serio programar según POO, es cuando una clase se hace presente.

Pues como supongo astutamente dedujo, la Programación Orientada a Objetos, consta de objetos, y una clase, define o es como la "plantilla" sobre la cual se construyen los tan mentados.

 

Constructores:

 

En una clase existe una función miembro muy particular llamada Constructor.

Un constructor es una función que debe tener el mismo nombre que la clase y no debe retornar ningún valor, (ni siquiera void), y se encarga de asignarle valores iniciales, (o simplemente inicializar), a los datos miembros.

En el ejemplo descubrirá que allí no hay ningún constructor definido, cuando ocurre esto el compilador de C++ crea en ejecución el constructor.

No obstante hubiera sido correcto haber definido un constructor que se encargara de, por ejemplo, inicializar con 0 los datos miembros.

Un constructor es invocado automáticamente cuando se crea la instancia, o sea que no hay llamarlo explícitamente desde el programa principal.

Existen 3 tipos de constructores:

 

- Constructor por defecto.

- Constructor común.

- Constructor de copia.

 

El constructor por defecto es, en caso que no lo haya definido, el que C++ en tiempo de ejecución le asigne, o bien:

 

class Coordenadas
{
    int x,y,z;


public:
    Coordenadas();     //Constructor por defecto

 

};


También le podríamos haber agregado a este constructor, encerrados entre llaves, los valores iniciales para los datos:

{x=0;y=0;z=0;}.

Cuando se crea el objeto se escribe: 

 

void main(void)

{

    Coordenadas coo;

    ....

}

 

El constructor común es aquel que recibe parámetros para asignarles como valores iniciales a los datos miembros, o sea que al crear la instancia, se pasó unos parámetros para inicializar.

 

class Coordenadas
{
    int x,y,z;


public:
    Coordenadas(int p, int q, int t) {x=p; y=q; z=t;}     //Constructor común.

 

};

 

Cuando se crea el objeto se escribe: 

 

void main(void)

{

    Coordenadas coo(6,7,22);        //Se le pasa los valores para inicializar.

    .....

}

 

El constructor de copia se utilizan para inicializar un objeto con otro objeto de la misma clase.

 

class Coordenadas
{
    int x,y,z;


public:
     Coordenadas ( int p, int q, int t) {x=p; y=q; z=t;}        //Constructor común.

    Coordenadas(const Coordenadas c)    //Constructor de copia.

        {

            x=c.x;

            y=c.y;

            z=c.z;

        }   

 

};

 

Cuando se crea el objeto se escribe: 

 

void main(void)

{

     Coordenadas k(1,2,3);                //Creación de un objeto  con lo valores iniciales 1, 2 y 3.

     Coordenadas coo=k;        //Se llama al constructor de copia para que le asigne a coo los valores de k.

     ....

}

 

Sobrecarga de funciones, (polimorfismo):

 

Habrá advertido en el último ejemplo de la clase, donde se ve el constructor de copia, que también se define un constructor común. Bueno, eso es posible, una clase puede tener varios constructores, que se irán usando de acuerdo a como uno cree el objeto, (pasándole o no parámetros).

Pero, observe nuevamente, esta vez más detalladamente, la clase..., ¿no encuentra otra cosa extraña?.

...(suspenso).  : )

 

Los constructores son funciones, ¿¿¿cómo permite el compilador dos funciones con el mismo nombre???.

Ahh, buena pregunta.

 

El compilador de C++ permitiría 100 funciones con el mismo nombre, el único requisito es que cada una de ellas tenga diferente número y/o tipo de parámetros.

Esta cualidad, que no se aplica solamente a los constructores y funciones miembros de una clase, sino que a cualquier función de un programa de C++, se llama Sobrecarga de funciones o Polimorfismo.

Cuando se llama a la función, C++ selecciona de todas las funciones sobrecargadas aquella que se ajusta de acuerdo con los parámetros pasados, en cantidad y tipo.

 

Funciones InLine:

 

También se puede estar preguntando, si las funciones miembros de una clase pueden estar definidas fuera de la clase.

La respuesta es sí, por lo general las funciones miembros están definidas fuera de la clase, dentro de esta última sólo se declararían los prototipos.

En el caso que la función esté definida dentro de la clase, ésta se llama función inline, como las funciones Cargar() y Mostrar() de nuestra clase Coordenadas. Se podría incluso agregar la cláusula inline, pero no hace falta.

¿Qué diferencia hay entre una función inline y otra, (definida dentro o fuera de la clase)?

Se define una función inline cuando es muy corta y simple, como los constructores y esas funciones del ejemplo. Declarar una función en línea significa que el compilador puede, si así lo decide, reemplazar cada invocación por la función, con la frecuencia que sea, por el código encerrado entre llaves.

Hay que tener en cuenta que funciones inline extensas consumen más memoria, a pesar que elimina el tiempo que lleva hacer la invocación.

Cuando se escribe una función fuera de la clase se especifica el acceso de la siguiente forma:

 

NombreClase::Función()    //Note que se accede con ::

 

Así quedaría nuestro programa, con la clase con un constructor por defecto y con las funciones miembro fuera de la clase.

 

#include <iostream.h>

class Coordenadas
{
    int x,y,z;
public:
    Coordenadas(){x=0;y=0;z=0;}     //Constructor por defecto.
    void Cargar(void);                         //Prototipo de las funciones.
    void Mostrar(void);
};

void Coordenadas::Cargar(void)        //Definición de las funciones fuera de la clase
{
    x=8;
    y=9;
    z=10;
}

void Coordenadas::Mostrar (void)
{
    cout << x <<endl;
    cout << y <<endl;
    cout << z <<endl;
}

void main(void)
{
    Coordenadas coo;

    coo.Cargar();
    coo.Mostrar();
}

 

Destructores:

 

Existe una función especial más para las clases, y se trata de los destructores.

Un destructor es una función miembro que se llama cuando se destruye la clase.

Todas las clases tiene un destructor implícito, incluso aunque no esté declarado. El destructor implícito no hace nada en particular, pero si uno quiere, puede declarar un destructor de forma explícita. Su sintaxis sería:

 

class NombreClase

{

    ...

 public:

        ~NombreClase();

        ...

}

 

El destructor debe comenzar con el caracter "ñuflo", (~), seguido por el nombre de la clase, (igual que el constructor). Además el destructor no puede recibir parámetros ni retornar nada, (ni siquiera void).

No puede haber más de un destructor para una clase y si no se define uno explícitamente, el compilador crea uno automáticamente.

El destructor se llama automáticamente siempre que una variable de ese tipo de clase, (una instancia u objeto), sale fuera de su ámbito, (por ejemplo cuando termina el programa).

 

Especificadores de acceso:

 

Ya había dicho que por defecto los datos miembros de una clase son privados. ¿Qué significa esto?.

Que sólo las funciones miembros públicas de la misma clase tienen acceso a ellos. Si lo desea puede escribir la cláusula private al momento de declarar los datos.

En cambio la cláusula public es obligatoria cuando se desea declarar un dato público y este dato estará disponible para cualquier función del programa.

Existe una cláusula más, protected. Los datos definidos a continuación de esta cláusula están restringidos para cualquier función externa a la clase, pero son públicos para la propia clase y los miembros de clases derivadas.

 

 

Siguiente capítulo (2)

Regresar a la página principal.