« Entrada anterior

a objetos con C++ (I)

 

 

 

 

 

El lenguaje C++ sigue siendo uno de los más utilizados para crear todo
tipo de aplicaciones. Aunque en laactualidad otras alternativas, como
Java o C#, han alcanzado granpopularidad, C++ sigue siendo el
lenguaje elegido cuando se deseancrear aplicaciones rápidas y robustas.
C++ marcó en su momento un puntode inflexión en la programación con
la orientación a objetos y siguesiendo una referencia indiscutible en
este campo.

 

 

 
 

uso de los punteros. En la actualidad muchas personas han aprendido la programación orientada a obje-
tos con lenguajes de generaciones posteriores a la de C++.

Estos presentan muchas ventajas. Laprincipal esque el programador puede en cierta medida ignorar
los procesos digamos de más bajo nivel inherentes atoda aplicación, como por ejemplo la reserva y liberación de memoria. Pero esta ventaja también puede llegar a convertirse en desventaja ya que la rapidez yrobustez de las aplicaciones dependen por completode la capacidad del lenguaje para gestionar eficientemente la memoria.

Con C++ la mayor parte de la responsabilidad reside en el programador: se gana en flexibilidad y capacidad de realizar todo tipo de tareas demanera extremadamente eficiente, y quizás se pierdeun poco en sencillez. Para los programadores que seenfrentan al aprendizaje de C++ desde el conocimiento de lenguajes como Java o C#, algunos conceptos pueden resultar desconcertantes al principio: direcciones de memoria que apuntan a variable, a funciones,liberación y reserva de la memoria, espacio que ocupan los objetos en la memoria, etc. Con un poco depaciencia y experiencia enseguida podrán observar la versatilidad y potencia de un lenguaje que prácticamente lo permite todo y que al mismo tiempo proporciona la orientación a objetos de una forma fiel al concepto original.

EL ENTORNO DE PROGRAMACIÓN

La mejor forma de aprender a programar es programando. Para ello es necesario un entorno de programación. Existen muchas alternativas gratuitas y depago. Uno de los entornos de programación mejores
es Eclipse IDE for C/C++ Developers.

En www.eclipse.org/downloads se puede descargar.
Además se necesita el software MinGW (www.mingw.org), que proporciona las herramientas necesarias para compilar y crear los ejecutables.

La descarga e instalación de las dos herramientas es muysencilla siguiendo los pasos indicados en las correspondientes páginas Web.

ESTRUCTURA DE UN PROGRAMA EN C++

La estructura de un programa en C++ simple se define de la siguiente manera:

 Directivas del preprocesador (include, define, etc.)

 Declaración de variables globales

 Prototipos de funciones

 Prototipos de clases

 Función main

 Definiciones de funciones

 Definiciones de clases

No hay que olvidar que la estructura descrita recoge el caso de un programa simple, es decir, un programa en el que existe un solo fichero de código fuente.

En las aplicaciones reales de cierta complejidad
existen más elementos a tener en cuenta: varios
ficheros de código fuente con sus correspondientes
ficheros de cabecera, librerías, etc. No obstante, por
muy complejo que sea el programa, al final la estructura básica en esencia es la misma.

El proyecto de C++ que va a realizarse para tomar contacto con el
lenguaje consta de tres ficheros: main.cpp,
GeomRectangle.cpp y GeomRectangle.h.

 El primer fichero de código fuente es el punto de entrada de la
aplicación. Los dos ficheros siguientes recogen la
funcionalidad de una clase, GeomRectangle, cuya
creación se estudiará a lo largo de este primer artículo.

Es probable que el programador que se
enfrenta por primera vez a un programa en
C++ encuentre muchas cosas en el código que
le desconcierten. No hay que desesperar ya que
todas ellas se irán descubriendo poco a poco
con el tiempo.

Lo importante en ese momento
inicial es poder empezar a crear pequeñas aplicaciones completamente funcionales para
aprender con la práctica las características
fundamentales del lenguaje. El esqueleto de
main.cpp es muy simple y sigue la estructura
definida anteriormente:

#include <cstdlib>
#include <iostream>
#include “GeomRectangle.h”
using namespace std;
int main(int argc, char *argv[]) {
···
}

En primer lugar se encuentran las directivas del
preprocesador. Con ellas se indica qué librerías
del sistema se van a utilizar. Las dos primeras,
cstdlib y iostream son librerías estándar que
entre otras muchas cosas permiten mostrar
información por la consola estándar. La tercera sentencia include es diferente. Con ella se
hace referencia al fichero de cabecera donde se
define una clase. Es decir, es en cierto un modo
una librería, pero de desarrollo propio.
Seguidamente aparece una sentencia using
namespace std que por el momento vamos a
ignorar. Por último está la función main, que es
el punto de entrada de la aplicación.

Los argumentos que recibe sirven para coger los parámetros

de entrada de la aplicación. El resultado que devuelve es un valor numérico que
indica cómo termina la aplicación. Por el
momento ninguno es relevante. Antes de pasar
a ver el código de la función main se va a estudiar la creación de la clase

GeomRectangle.

DEFINICIÓN DE UNA CLASE

En el fichero GeomRectangle.h se encuentra
la definición de la clase. Una clase se define
con la palabra reservada class:

class GeomRectangle {
private:
/* ··· *
Los métodos de una clase pueden definirse
dentro de la estructura class o pueden defi-

nirse fuera. En este ejemplo hay métodos
escritos de las dos formas. Todos son públi-

cos. El primer método se denomina
setPosition y sirve para establecer la posición
del rectángulo en el plano. Recibe como
parámetros dos valores numéricos, x e y, con
las coordenadas:

void setPosition(int x, int y) {
this->x = x;
this->y = y;
}

La palabra reservada this seguida del opera-

dor flecha (->) se utiliza para acceder a los
miembros de la clase evitando la confusión
que podría producirse debido a que los nom-

bres de los parámetros del método y los
nombres de los miembros de la clase son
iguales. El siguiente método es getPosition,
que sirve para obtener la posición actual del
rectángulo. Los parámetros que recibe el
método son igualmente x e y pero ahora se
utiliza el operador de dirección (&) lo que sig-

nifica que en realidad se están pasando las
direcciones de memoria de x e y:

void getPosition(int &x, int &y) {
x = this->x;
y = this->y;
}

El código es muy simple. Se asignan a x e y
los valores de los miembros de la clase. Es
decir, x e y son en este caso parámetros de
salida. Los métodos moveHor y moveVer se

utilizan para mover el rectángulo horizontal
y verticalmente:

void moveHor(int d) {
this->x = this->x + d;
}
void moveVer(int d) {
this->y = this->y + d;
}

Los métodos makeSmaller y makeBigger se
emplean para hacer el rectángulo más grande

o más pequeño, pero sin moverlo de su posi-

ción. Como aspecto interesante cabe señalar

que en este caso se accede a los miembros de

la clase directamente, sin utiliza la expresión

this->. Éste es el método habitual. La expresión

this-> se ha empleado en los métodos anterio-

res porque los nombres de los parámetros y los
nombres de los miembros de la clase eran
iguales. Pero aquí no se produce esa confusión:

void makeSmaller() {
x = x + (4/2);
y = y + (4/2);
w = w – 4;
h = h – 4;
}
void makeBigger() {
x = x – (4/2);
y = y – (4/2);
w = w + 4;
h = h + 4;
}

Finalmente los métodos setDimension y
getDimension, a imagen y semejanza de
setPosition y getPosition, devuelven el

 tamaño del rectángulo. El código de estos dos
métodos está fuera de la clase por lo que en
ella sólo se encuentran las cabeceras:

void setDimension(int w, int h);
void getDimension(int &w, int &h);

El fichero GeomRectangle.cpp, que podríamos

denominar de implementación de la clase, con-

tiene precisamente el código correspondiente

de todo aquello de la clase que no se ha imple-

mentado en la propia definición de la clase.

include “GeomRectangle.h”

void GeomRectangle::setDimension(int w,
int h) {
this->w = w;
this->h = h;
};
void GeomRectangle::getDimension(int &w,
int &h) {
w = this->w;
h = this->h;
}

Los métodos se escriben precedidos del nom-

bre de la clase y dos puntos. De esta forma

queda explicitado que en realidad se están

definiendo métodos de una clase dada, a la
que además se hace referencia la directiva
include del preprocesador.

 UTILIZACIÓN DE LOS OBJETOS

Hasta aquí se ha visto la creación de una clase muy simple con la que ya se puede empezar a jugar en la función main de la aplicación. Pero antes hay que fijar algunos conceptos básicos en relación a la memoria y el ciclo de vida de los objetos

ESPACIOS DE MEMORIA: LA PILA Y EL MONTÓN

Cada vez que se ejecuta un programa se reserva un espacio de memoria denominado pila, en inglés stack. El tamaño de la pila es fijo y se define en el tiempo de enlazado (linking).

Entre otras tareas la pila se emplea para almacenar las variables locales y los parámetros de las funciones. La principal ventaja de la pila, como estructura en el modelo de memoria de un proceso, es la rapidez.

Los dos inconvenientes principales es que su tamaño es fijo, demasiado pequeño para algunas aplicaciones, y además sólo es posible almacenar variables de tamaño conocido en tiempo compilación.

Los programas pueden emplear otro tipo diferente de espacio en memoria que se denomina montón, en inglés heap.

El montón es una zona reservada de la memoria donde se van almacenando variables, estructuras, objetos, etc. No establece ningún orden de entrada o salida de sus elementos.

El tamaño de esta zona de memoria suele ser mayor que el de la pila, el programador lo define y lo puede ampliar en cualquier momento. Conindependencia del tipo y tamaño de los datos guardados en el montón, sólo es posible acceder a ellos a través de un puntero. En C++ una variable que se define en el código como:

GeomRectangle geomRect1;

 

 representa a un objeto que se almacena en la pila. El acceso a las propiedades y los métodos del objeto se realiza con el operador punto (.). Por ejemplo:

geomRect1.setDimension(320, 200);

 En cambio una variable que se define como:

GeomRectangle* geomRect2;

 

representa a un objeto que se almacena en el montón. El acceso a las propiedades y los métodos del objeto se realiza con el operador fecha (->). Por ejemplo:

geomRect2->setDimension(320, 200);

 

En realidad el operador flecha es una combinación de dos operadores: el operador asterisco (*) que dada una posición de memoria, un puntero, permite acceder al dato almacenado en dicha posición; y el operador punto (.), que dado un objeto permite acceder a sus propiedades y métodos.

Por ello la expresión anterior podría rescribirse como:

(*geomRect2).setDimension(320, 200); 

 Los objetos creados en la pila se crean en el mismo momento que se declaran, de forma transparente al programador.

Es decir, la expresión GeomRectangle geomRect1 provoca implícitamente que se cree un nuevo objeto de tipo GeomRectangle, reservando el espacio necesario en el área de memoria correspondiente a la pila, de forma transparente.

 Cuando el programa termina, la pila desaparece, y con ello la memoria utilizada por el objeto se libera, también de forma transparente.

En cambio los objetos creados en el montón deben crearse y eliminarse explícitamente. La creación se lleva a cabo con la palabra reservada newy la eliminación con la palabra reservada delete:

 geomRect2 = new GeomRectangle();

 ··· 

delete geomRect2;

 CICLO DE VIDA DE UN OBJETO

 El ciclo de vida de un objeto viene marcado por tres tareas distintas: creación, asignación y destrucción.

Es de vital importancia conocer bien el ciclo de vida de los objetos en C++ para llegar a programar aplicaciones eficientes. Los objetos se crean cuando se declaran (si se trata de objetos que se almacenan en la pila) o bien cuando se utiliza explícitamente el operador new.

Los constructores son las funciones de los objetos que se utilizan durante su creación para inicializarlos. En el código, un constructor es una función que tiene el mismo nombre que la clase y que no devuelve nada.

La implementación de un constructor está sujeta a las mimas reglas que la implementación de los métodos. Por ejemplo:

class GeomRectangle { 
private:
int x, y, w, h;
public:
GeomRectangle() {
x = 0; y = 0; w = 0; h = 0;
}
GeomRectangle(int x, int y, int w, int h) {
this->x = x; this->y = y;
this->w = w; this->h = h;
} ···
}

En esta nueva definición de la clase GeomRectangle hay dos constructores: el primero no recibe parámetros, por lo que se denomina también el constructor por defecto; el segundo recibe cuatro parámetros que se emplean para inicializar las propiedades del objeto. Los constructores se utilizan tanto para aquellos objetos que se almacenan en la pila como para aquellos otros que se almacenan en el montón:

 GeomRectangle geomRect3(0, 0, 320, 200); 
geomRect4 = new GeomRectangle(0, 0, 320, 200);

Una clase puede definir tantos constructores como sea necesario. E incluso es posible que la implementación de un constructor termine llamando a otro. Por ejemplo:

GeomRectangle(int x, int y) { 
GeomRectangle(x, y, 0, 0); 
}

 Existe un tipo especial de constructor denominado de copia, en inglés copy constructor. Se utiliza que cuando se desea crear un objeto que sea una copia idéntica de otro. Si el programador no escribe este constructor C++ asigna uno por defecto.

En C++ por defecto los parámetros se pasan a las funciones por parámetro. Esto significa que la función recibe una copia de la variable que se pasa como parámetro, no la variable misma.

 Por lo tanto cada vez que se pasa como parámetro un objeto se crea una copia del mismo con el constructor de copia.

Con el fin de evitar que se estén creando dinámicamente copias de objetos las funciones se pueden declarar de forma reciban una referencia al objeto que se pasa como parámetro, en vez de una copia. Es lo que se conoce como el paso de parámetros por referencia.

Cuando se pasa a una función un objeto por referencia en realidad se está pasando la dirección de memoria de dicho objeto.

Las direcciones de memoria en C++ son punteros, y los punteros también pueden referenciar variables locales.

El operador de dirección (&) permite obtener la dirección de memoria donde se almacena una variable dada. El paso de parámetros por referencia ahorra memoria y tiempo, pero plantea un posible problema ya que la función puede modificar el objeto original.

 Para evitarlo se utiliza la palabra reservada const, que evita que el objeto original pueda ser modificado.

Cuando un objeto va a ser destruido tienen lugar dos tareas: el destructor del objeto se ejecuta y la memoria que dicho objeto estaba utilizando se libera. No es obligatorio hacer un destructor, de la misma forma que no es obligatorio hacer un constructor.

Normalmente los destructores se escriben para liberar recursos obtenidos dinámicamente (memoria, ficheros, acceso a base de datos, sockets, etc.) justo antes de que el objeto se destruya.

Si una clase no tiene destructor el sistema le asigna uno por defecto. Los objetos que se almacenan en la pila se destruyen automáticamente simplemente cuando ya no se necesitan.

 Por ejemplo, si el objeto se crea dentro de una sentencia if, al finalizar dicha sentencia el objeto se destruye.

En cambio los objetos que se almacenan en el montón deben ser destruidos explícitamente utilizando la palabra reservada delete.

 De la misma forma que ocurre con los tipos simples en C++ se pueden asignar unos objetos a otros. Aunque esta tarea se lleva a cabo con el operador de asignación (=) en realidad se utiliza una función de especial de objetos, a imagen y semejanza de lo que ocurre con funciones como el constructor, el destructor o el constructor de copia.

 

PARA VER EL DOCUMENTO ORIGINAL PINCHA AQUI

 
 

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Entrada siguiente »